mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-20 22:26:54 +01:00
Merge branch 'android_branch' into android_translate
This commit is contained in:
commit
a3fb82ebc4
84 changed files with 3187 additions and 1672 deletions
10
.travis.yml
10
.travis.yml
|
@ -10,7 +10,11 @@ android:
|
||||||
- tools
|
- tools
|
||||||
- platform-tools
|
- platform-tools
|
||||||
- build-tools-27.0.3
|
- build-tools-27.0.3
|
||||||
- android-23
|
- android-26
|
||||||
|
licenses:
|
||||||
|
- android-sdk-preview-license-.+
|
||||||
|
- android-sdk-license-.+
|
||||||
|
- google-gdk-license-.+
|
||||||
before_script:
|
before_script:
|
||||||
- export TERM=dumb
|
- export TERM=dumb
|
||||||
- curl -L http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin -O
|
- curl -L http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin -O
|
||||||
|
@ -23,18 +27,18 @@ before_script:
|
||||||
- export PATH=$PATH:${ANDROID_NDK_HOME}
|
- export PATH=$PATH:${ANDROID_NDK_HOME}
|
||||||
- cd xwords4/android/
|
- cd xwords4/android/
|
||||||
before_install:
|
before_install:
|
||||||
|
- yes | sdkmanager "platforms;android-27"
|
||||||
- openssl aes-256-cbc -K $encrypted_8436f2891714_key -iv $encrypted_8436f2891714_iv
|
- openssl aes-256-cbc -K $encrypted_8436f2891714_key -iv $encrypted_8436f2891714_iv
|
||||||
-in id_rsa_uploader.enc -out /tmp/id_rsa_uploader -d
|
-in id_rsa_uploader.enc -out /tmp/id_rsa_uploader -d
|
||||||
- chmod 600 \/tmp\/id_rsa_uploader
|
- chmod 600 \/tmp\/id_rsa_uploader
|
||||||
- sudo apt-get -qq update
|
- sudo apt-get -qq update
|
||||||
- sudo apt-get install -y python-lxml imagemagick
|
- sudo apt-get install -y python-lxml imagemagick
|
||||||
script:
|
script:
|
||||||
- "./gradlew -PuseCrashlytics assXw4dDeb"
|
- "./gradlew -PuseCrashlytics asXw4dDeb"
|
||||||
- scp -o "StrictHostKeyChecking no" -i /tmp/id_rsa_uploader -d app/build/outputs/apk/xw4d/debug/*.apk
|
- scp -o "StrictHostKeyChecking no" -i /tmp/id_rsa_uploader -d app/build/outputs/apk/xw4d/debug/*.apk
|
||||||
uploader@eehouse.org:XW4D_UPLOAD
|
uploader@eehouse.org:XW4D_UPLOAD
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
recipients:
|
recipients:
|
||||||
- xwords@eehouse.org
|
- xwords@eehouse.org
|
||||||
on_success: always
|
|
||||||
on_failure: always
|
on_failure: always
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
def INITIAL_CLIENT_VERS = 8
|
def INITIAL_CLIENT_VERS = 8
|
||||||
def VERSION_CODE_BASE = 132
|
def VERSION_CODE_BASE = 133
|
||||||
def VERSION_NAME = '4.4.136'
|
def VERSION_NAME = '4.4.137'
|
||||||
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
|
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
|
||||||
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
|
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
|
||||||
def BUILD_INFO_NAME = "build-info.txt"
|
def BUILD_INFO_NAME = "build-info.txt"
|
||||||
|
@ -24,6 +24,7 @@ if ( FABRIC_API_KEY && hasProperty('useCrashlytics') ) {
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://maven.fabric.io/public' }
|
maven { url 'https://maven.fabric.io/public' }
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -32,7 +33,7 @@ android {
|
||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '27.0.3'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 8
|
minSdkVersion 8
|
||||||
targetSdkVersion 23
|
targetSdkVersion 26
|
||||||
versionCode VERSION_CODE_BASE
|
versionCode VERSION_CODE_BASE
|
||||||
versionName VERSION_NAME
|
versionName VERSION_NAME
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,16 @@
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<provider android:name="android.support.v4.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
>
|
||||||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths"
|
||||||
|
/>
|
||||||
|
</provider>
|
||||||
|
|
||||||
<activity android:name="MainActivity"
|
<activity android:name="MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="standard"
|
android:launchMode="standard"
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>CrossWords 4.4.136 release</h2>
|
<h2>CrossWords 4.4.137 release</h2>
|
||||||
|
|
||||||
<p>This release fixes a couple of rare bugs.</p>
|
<p>This release paves the way for play-via-SMS improvements.</p>
|
||||||
|
|
||||||
<div id="survey">
|
<div id="survey">
|
||||||
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
|
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
|
||||||
|
@ -25,9 +25,12 @@
|
||||||
|
|
||||||
<h3>New with this release</h3>
|
<h3>New with this release</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Flip arrow to match when board flipped</li>
|
<li>Add new SMS-handling code. One feature isn't compatible with
|
||||||
<li>Fix problems when undos arrive at unexpected times</li>
|
the previous release so it won't be activated until there's been
|
||||||
<li>Don't crash when game connects while app's in background</li>
|
time for everybody to upgrade.</li>
|
||||||
|
<li>Include new Catalan translations</li>
|
||||||
|
<li>Fix bug causing (rarely) unending notifications of wordlist
|
||||||
|
upgrade</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>(The full changelog
|
<p>(The full changelog
|
||||||
|
|
|
@ -175,8 +175,7 @@ public class DictBrowseDelegate extends DelegateBase
|
||||||
String[] names = { name };
|
String[] names = { name };
|
||||||
DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, names );
|
DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, names );
|
||||||
m_dictClosure = XwJNI.dict_iter_init( pairs.m_bytes[0],
|
m_dictClosure = XwJNI.dict_iter_init( pairs.m_bytes[0],
|
||||||
name, pairs.m_paths[0],
|
name, pairs.m_paths[0] );
|
||||||
JNIUtilsImpl.get(m_activity) );
|
|
||||||
|
|
||||||
m_browseState = DBUtils.dictsGetOffset( m_activity, name, m_loc );
|
m_browseState = DBUtils.dictsGetOffset( m_activity, name, m_loc );
|
||||||
boolean newState = null == m_browseState;
|
boolean newState = null == m_browseState;
|
||||||
|
@ -280,7 +279,7 @@ public class DictBrowseDelegate extends DelegateBase
|
||||||
{
|
{
|
||||||
TextView text = (TextView)view;
|
TextView text = (TextView)view;
|
||||||
// null text seems to have generated at least one google play report
|
// null text seems to have generated at least one google play report
|
||||||
if ( null != text ) {
|
if ( null != text && null != m_browseState ) {
|
||||||
int newval = Integer.parseInt( text.getText().toString() );
|
int newval = Integer.parseInt( text.getText().toString() );
|
||||||
switch ( parent.getId() ) {
|
switch ( parent.getId() ) {
|
||||||
case R.id.wordlen_min:
|
case R.id.wordlen_min:
|
||||||
|
|
|
@ -486,7 +486,6 @@ public class DictLangCache {
|
||||||
info = new DictInfo();
|
info = new DictInfo();
|
||||||
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
||||||
pairs.m_paths[0],
|
pairs.m_paths[0],
|
||||||
JNIUtilsImpl.get( context ),
|
|
||||||
DictLoc.DOWNLOAD == dal.loc,
|
DictLoc.DOWNLOAD == dal.loc,
|
||||||
info ) ) {
|
info ) ) {
|
||||||
|
|
||||||
|
|
|
@ -151,15 +151,17 @@ public class DictUtils {
|
||||||
// changes?
|
// changes?
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addLogDup( Map<String, DictAndLoc> map, String path,
|
private static void addLogDupIf( Context context, Map<String, DictAndLoc> map,
|
||||||
DictLoc loc )
|
String path, File dir, DictLoc loc )
|
||||||
{
|
{
|
||||||
|
if ( isDict( context, path, dir ) ) {
|
||||||
String name = removeDictExtn( new File(path).getName() );
|
String name = removeDictExtn( new File(path).getName() );
|
||||||
if ( map.containsKey( name ) ) {
|
if ( map.containsKey( name ) ) {
|
||||||
Log.d( TAG, "replacing info for %s with from %s", name, loc );
|
Log.d( TAG, "replacing info for %s with from %s", name, loc );
|
||||||
}
|
}
|
||||||
map.put( name, new DictAndLoc( name, loc ) );
|
map.put( name, new DictAndLoc( name, loc ) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void tryDir( Context context, File dir, boolean strict,
|
private static void tryDir( Context context, File dir, boolean strict,
|
||||||
DictLoc loc, Map<String, DictAndLoc> map )
|
DictLoc loc, Map<String, DictAndLoc> map )
|
||||||
|
@ -168,9 +170,7 @@ public class DictUtils {
|
||||||
String[] list = dir.list();
|
String[] list = dir.list();
|
||||||
if ( null != list ) {
|
if ( null != list ) {
|
||||||
for ( String file : list ) {
|
for ( String file : list ) {
|
||||||
if ( isDict( context, file, strict? dir : null ) ) {
|
addLogDupIf( context, map, file, strict? dir : null, loc );
|
||||||
addLogDup( map, file, loc );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,15 +190,11 @@ public class DictUtils {
|
||||||
Map<String, DictAndLoc> map = new HashMap<>();
|
Map<String, DictAndLoc> map = new HashMap<>();
|
||||||
|
|
||||||
for ( String file : getAssets( context ) ) {
|
for ( String file : getAssets( context ) ) {
|
||||||
if ( isDict( context, file, null ) ) {
|
addLogDupIf( context, map, file, null, DictLoc.BUILT_IN );
|
||||||
addLogDup( map, file, DictLoc.BUILT_IN );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( String file : context.fileList() ) {
|
for ( String file : context.fileList() ) {
|
||||||
if ( isDict( context, file, null ) ) {
|
addLogDupIf( context, map, file, null, DictLoc.INTERNAL );
|
||||||
addLogDup( map, file, DictLoc.INTERNAL );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, map );
|
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, map );
|
||||||
|
@ -578,7 +574,7 @@ public class DictUtils {
|
||||||
if ( ok && null != dir ) {
|
if ( ok && null != dir ) {
|
||||||
String fullPath = new File( dir, file ).getPath();
|
String fullPath = new File( dir, file ).getPath();
|
||||||
ok = XwJNI.dict_getInfo( null, removeDictExtn( file ), fullPath,
|
ok = XwJNI.dict_getInfo( null, removeDictExtn( file ), fullPath,
|
||||||
JNIUtilsImpl.get(context), true, null );
|
true, null );
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class DwnldDelegate extends ListDelegateBase {
|
||||||
callListener( m_uri, true );
|
callListener( m_uri, true );
|
||||||
} else if ( null != m_appFile ) {
|
} else if ( null != m_appFile ) {
|
||||||
// launch the installer
|
// launch the installer
|
||||||
Intent intent = Utils.makeInstallIntent( m_appFile );
|
Intent intent = Utils.makeInstallIntent( m_activity, m_appFile );
|
||||||
startActivity( intent );
|
startActivity( intent );
|
||||||
} else {
|
} else {
|
||||||
// we failed at something....
|
// we failed at something....
|
||||||
|
@ -473,5 +473,4 @@ public class DwnldDelegate extends ListDelegateBase {
|
||||||
intent.putExtra( APK_EXTRA, url );
|
intent.putExtra( APK_EXTRA, url );
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
@ -131,8 +131,8 @@ public class GameUtils {
|
||||||
|
|
||||||
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes, pairs.m_paths,
|
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes, pairs.m_paths,
|
||||||
gi.langName( context ), (UtilCtxt)null,
|
gi.langName( context ), (UtilCtxt)null,
|
||||||
JNIUtilsImpl.get( context ), (DrawCtx)null,
|
(DrawCtx)null, CommonPrefs.get( context ),
|
||||||
CommonPrefs.get( context ), (TransportProcs)null );
|
(TransportProcs)null );
|
||||||
|
|
||||||
if ( juggle ) {
|
if ( juggle ) {
|
||||||
gi.juggle();
|
gi.juggle();
|
||||||
|
@ -372,15 +372,13 @@ public class GameUtils {
|
||||||
gamePtr = XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
gamePtr = XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||||
pairs.m_bytes, pairs.m_paths,
|
pairs.m_bytes, pairs.m_paths,
|
||||||
langName, util,
|
langName, util,
|
||||||
JNIUtilsImpl.get( context ),
|
|
||||||
null,
|
null,
|
||||||
CommonPrefs.get(context),
|
CommonPrefs.get(context),
|
||||||
tp );
|
tp );
|
||||||
if ( null == gamePtr ) {
|
if ( null == gamePtr ) {
|
||||||
gamePtr = XwJNI.initNew( gi, dictNames,
|
gamePtr = XwJNI.initNew( gi, dictNames,
|
||||||
pairs.m_bytes, pairs.m_paths,
|
pairs.m_bytes, pairs.m_paths,
|
||||||
langName, (UtilCtxt)null,
|
langName, (UtilCtxt)null, null,
|
||||||
JNIUtilsImpl.get(context), null,
|
|
||||||
CommonPrefs.get(context), null );
|
CommonPrefs.get(context), null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,12 +483,12 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( force ) {
|
if ( force ) {
|
||||||
new ResendTask( context, filter, proc ).execute();
|
|
||||||
|
|
||||||
System.arraycopy( sendTimes, 0, /* src */
|
System.arraycopy( sendTimes, 0, /* src */
|
||||||
sendTimes, 1, /* dest */
|
sendTimes, 1, /* dest */
|
||||||
sendTimes.length - 1 );
|
sendTimes.length - 1 );
|
||||||
sendTimes[0] = now;
|
sendTimes[0] = now;
|
||||||
|
|
||||||
|
new Resender( context, filter, proc ).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,6 +912,7 @@ public class GameUtils {
|
||||||
m_gotMsg = false;
|
m_gotMsg = false;
|
||||||
m_gameOver = false;
|
m_gameOver = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
||||||
{
|
{
|
||||||
|
@ -923,11 +922,14 @@ public class GameUtils {
|
||||||
m_chat = msg;
|
m_chat = msg;
|
||||||
m_ts = tsSeconds;
|
m_ts = tsSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void turnChanged( int newTurn )
|
public void turnChanged( int newTurn )
|
||||||
{
|
{
|
||||||
m_gotMsg = true;
|
m_gotMsg = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notifyGameOver()
|
public void notifyGameOver()
|
||||||
{
|
{
|
||||||
m_gameOver = true;
|
m_gameOver = true;
|
||||||
|
@ -1024,8 +1026,7 @@ public class GameUtils {
|
||||||
XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||||
pairs.m_bytes, pairs.m_paths,
|
pairs.m_bytes, pairs.m_paths,
|
||||||
gi.langName( context ), null,
|
gi.langName( context ), null,
|
||||||
JNIUtilsImpl.get(context), null,
|
null, CommonPrefs.get( context ), null );
|
||||||
CommonPrefs.get( context ), null );
|
|
||||||
// second time required as game_makeFromStream can overwrite
|
// second time required as game_makeFromStream can overwrite
|
||||||
gi.replaceDicts( context, newDict );
|
gi.replaceDicts( context, newDict );
|
||||||
|
|
||||||
|
@ -1075,8 +1076,7 @@ public class GameUtils {
|
||||||
new CurGameInfo(context),
|
new CurGameInfo(context),
|
||||||
dictNames, pairs.m_bytes,
|
dictNames, pairs.m_bytes,
|
||||||
pairs.m_paths, langName,
|
pairs.m_paths, langName,
|
||||||
null, JNIUtilsImpl.get(context),
|
null, null, cp, null );
|
||||||
null, cp, null );
|
|
||||||
madeGame = null != gamePtr;
|
madeGame = null != gamePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1084,8 +1084,7 @@ public class GameUtils {
|
||||||
Assert.assertNull( gamePtr );
|
Assert.assertNull( gamePtr );
|
||||||
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes,
|
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes,
|
||||||
pairs.m_paths, langName, util,
|
pairs.m_paths, langName, util,
|
||||||
JNIUtilsImpl.get(context), (DrawCtx)null,
|
(DrawCtx)null, cp, sink );
|
||||||
cp, sink );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( null != car ) {
|
if ( null != car ) {
|
||||||
|
@ -1255,23 +1254,27 @@ public class GameUtils {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ResendTask extends AsyncTask<Void, Void, Void> {
|
private static class Resender extends Thread {
|
||||||
private Context m_context;
|
private Context m_context;
|
||||||
private ResendDoneProc m_doneProc;
|
private ResendDoneProc m_doneProc;
|
||||||
private CommsConnType m_filter;
|
private CommsConnType m_filter;
|
||||||
private int m_nSent = 0;
|
private Handler m_handler;
|
||||||
|
|
||||||
public ResendTask( Context context, CommsConnType filter,
|
public Resender( Context context, CommsConnType filter,
|
||||||
ResendDoneProc proc )
|
ResendDoneProc proc )
|
||||||
{
|
{
|
||||||
m_context = context;
|
m_context = context;
|
||||||
m_filter = filter;
|
m_filter = filter;
|
||||||
m_doneProc = proc;
|
m_doneProc = proc;
|
||||||
|
if ( null != proc ) {
|
||||||
|
m_handler = new Handler();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground( Void... unused )
|
public void run()
|
||||||
{
|
{
|
||||||
|
int nSentTotal = 0;
|
||||||
HashMap<Long,CommsConnTypeSet> games
|
HashMap<Long,CommsConnTypeSet> games
|
||||||
= DBUtils.getGamesWithSendsPending( m_context );
|
= DBUtils.getGamesWithSendsPending( m_context );
|
||||||
|
|
||||||
|
@ -1296,11 +1299,11 @@ public class GameUtils {
|
||||||
int nSent = XwJNI.comms_resendAll( gamePtr, true,
|
int nSent = XwJNI.comms_resendAll( gamePtr, true,
|
||||||
m_filter, false );
|
m_filter, false );
|
||||||
gamePtr.release();
|
gamePtr.release();
|
||||||
Log.d( TAG, "ResendTask.doInBackground(): sent %d "
|
Log.d( TAG, "Resender.doInBackground(): sent %d "
|
||||||
+ "messages for rowid %d", nSent, rowid );
|
+ "messages for rowid %d", nSent, rowid );
|
||||||
m_nSent += sink.numSent();
|
nSentTotal += sink.numSent();
|
||||||
} else {
|
} else {
|
||||||
Log.d( TAG, "ResendTask.doInBackground(): loadMakeGame()"
|
Log.d( TAG, "Resender.doInBackground(): loadMakeGame()"
|
||||||
+ " failed for rowid %d", rowid );
|
+ " failed for rowid %d", rowid );
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
@ -1311,19 +1314,21 @@ public class GameUtils {
|
||||||
false, false );
|
false, false );
|
||||||
jniThread.release();
|
jniThread.release();
|
||||||
} else {
|
} else {
|
||||||
Log.w( TAG, "ResendTask.doInBackground: unable to unlock %d",
|
Log.w( TAG, "Resender.doInBackground: unable to unlock %d",
|
||||||
rowid );
|
rowid );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute( Void unused )
|
|
||||||
{
|
|
||||||
if ( null != m_doneProc ) {
|
if ( null != m_doneProc ) {
|
||||||
m_doneProc.onResendDone( m_context, m_nSent );
|
final int fSentTotal = nSentTotal;
|
||||||
|
m_handler
|
||||||
|
.post( new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
m_doneProc.onResendDone( m_context, fSentTotal );
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1451,8 +1451,6 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
showItemsIf( ONEGAME_ITEMS, menu, 1 == nGamesSelected );
|
showItemsIf( ONEGAME_ITEMS, menu, 1 == nGamesSelected );
|
||||||
showItemsIf( ONEGROUP_ITEMS, menu, 1 == nGroupsSelected );
|
showItemsIf( ONEGROUP_ITEMS, menu, 1 == nGroupsSelected );
|
||||||
|
|
||||||
// check for updates only serves release builds, so don't offer in
|
|
||||||
// DEBUG case
|
|
||||||
boolean enable = showDbg && nothingSelected
|
boolean enable = showDbg && nothingSelected
|
||||||
&& UpdateCheckReceiver.haveToCheck( m_activity );
|
&& UpdateCheckReceiver.haveToCheck( m_activity );
|
||||||
Utils.setItemVisible( menu, R.id.games_menu_checkupdates, enable );
|
Utils.setItemVisible( menu, R.id.games_menu_checkupdates, enable );
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||||
import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType;
|
import org.eehouse.android.xw4.jni.DUtilCtxt.DevIDType;
|
||||||
import org.eehouse.android.xw4.jni.XwJNI;
|
import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
/* -*- compile-command: "find-and-gradle.sh inXw4Deb"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2010 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 2010 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -40,6 +40,7 @@ import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||||
|
import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -55,19 +56,11 @@ import java.util.Set;
|
||||||
public class SMSService extends XWService {
|
public class SMSService extends XWService {
|
||||||
private static final String TAG = SMSService.class.getSimpleName();
|
private static final String TAG = SMSService.class.getSimpleName();
|
||||||
|
|
||||||
private static final String INSTALL_URL = "http://eehouse.org/_/a.py/a ";
|
|
||||||
private static final int MAX_SMS_LEN = 140; // ??? differs by network
|
|
||||||
private static final int KITKAT = 19;
|
|
||||||
|
|
||||||
private static final String MSG_SENT = "MSG_SENT";
|
private static final String MSG_SENT = "MSG_SENT";
|
||||||
private static final String MSG_DELIVERED = "MSG_DELIVERED";
|
private static final String MSG_DELIVERED = "MSG_DELIVERED";
|
||||||
|
|
||||||
private static final int SMS_PROTO_VERSION_ORIG = 0;
|
|
||||||
private static final int SMS_PROTO_VERSION_WITHPORT = 1;
|
private static final int SMS_PROTO_VERSION_WITHPORT = 1;
|
||||||
private static final int SMS_PROTO_VERSION = SMS_PROTO_VERSION_WITHPORT;
|
private static final int SMS_PROTO_VERSION = SMS_PROTO_VERSION_WITHPORT;
|
||||||
private static final int MAX_LEN_TEXT = 100;
|
|
||||||
private static final int MAX_LEN_BINARY = 100;
|
|
||||||
private static final int MAX_MSG_COUNT = 16; // 1.6K enough? Should be....
|
|
||||||
private enum SMSAction { _NONE,
|
private enum SMSAction { _NONE,
|
||||||
INVITE,
|
INVITE,
|
||||||
SEND,
|
SEND,
|
||||||
|
@ -75,6 +68,7 @@ public class SMSService extends XWService {
|
||||||
ADDED_MISSING,
|
ADDED_MISSING,
|
||||||
STOP_SELF,
|
STOP_SELF,
|
||||||
HANDLEDATA,
|
HANDLEDATA,
|
||||||
|
RESEND,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String CMD_STR = "CMD";
|
private static final String CMD_STR = "CMD";
|
||||||
|
@ -98,8 +92,6 @@ public class SMSService extends XWService {
|
||||||
|
|
||||||
private int m_nReceived = 0;
|
private int m_nReceived = 0;
|
||||||
private static int s_nSent = 0;
|
private static int s_nSent = 0;
|
||||||
private static Map<String, HashMap <Integer, MsgStore>> s_partialMsgs
|
|
||||||
= new HashMap<String, HashMap <Integer, MsgStore>>();
|
|
||||||
private static Set<Integer> s_sentDied = new HashSet<Integer>();
|
private static Set<Integer> s_sentDied = new HashSet<Integer>();
|
||||||
|
|
||||||
public static class SMSPhoneInfo {
|
public static class SMSPhoneInfo {
|
||||||
|
@ -366,6 +358,9 @@ public class SMSService extends XWService {
|
||||||
phone = intent.getStringExtra( PHONE );
|
phone = intent.getStringExtra( PHONE );
|
||||||
sendDiedPacket( phone, gameID );
|
sendDiedPacket( phone, gameID );
|
||||||
break;
|
break;
|
||||||
|
case RESEND:
|
||||||
|
phone = intent.getStringExtra( PHONE );
|
||||||
|
resendFor( phone, null, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,18 +429,18 @@ public class SMSService extends XWService {
|
||||||
dos.writeInt( gameID );
|
dos.writeInt( gameID );
|
||||||
dos.write( bytes, 0, bytes.length );
|
dos.write( bytes, 0, bytes.length );
|
||||||
dos.flush();
|
dos.flush();
|
||||||
if ( send( SMS_CMD.DATA, bas.toByteArray(), phone ) ) {
|
send( SMS_CMD.DATA, bas.toByteArray(), phone );
|
||||||
nSent = bytes.length;
|
nSent = bytes.length;
|
||||||
}
|
|
||||||
} catch ( java.io.IOException ioe ) {
|
} catch ( java.io.IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
}
|
}
|
||||||
return nSent;
|
return nSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean send( SMS_CMD cmd, byte[] bytes, String phone )
|
private void send( SMS_CMD cmd, byte[] bytes, String phone )
|
||||||
throws java.io.IOException
|
throws java.io.IOException
|
||||||
{
|
{
|
||||||
|
Log.d( TAG, "send(%s, len=%d)", cmd, bytes.length );
|
||||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||||
DataOutputStream dos = new DataOutputStream( bas );
|
DataOutputStream dos = new DataOutputStream( bas );
|
||||||
dos.writeByte( SMS_PROTO_VERSION );
|
dos.writeByte( SMS_PROTO_VERSION );
|
||||||
|
@ -457,42 +452,44 @@ public class SMSService extends XWService {
|
||||||
dos.flush();
|
dos.flush();
|
||||||
|
|
||||||
byte[] data = bas.toByteArray();
|
byte[] data = bas.toByteArray();
|
||||||
byte[][] msgs = breakAndEncode( data );
|
|
||||||
boolean result = null != msgs && sendBuffers( msgs, phone );
|
boolean newSMSEnabled = XWPrefs.getSMSProtoEnabled( this );
|
||||||
return result;
|
boolean forceNow = !newSMSEnabled; // || cmd == SMS_CMD.INVITE;
|
||||||
|
resendFor( phone, data, forceNow );
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[][] breakAndEncode( byte msg[] ) throws java.io.IOException
|
private void resendFor( String phone, byte[] data, boolean forceNow )
|
||||||
{
|
{
|
||||||
byte[][] result = null;
|
int[] waitSecs = { 0 };
|
||||||
int count = (msg.length + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY;
|
byte[][] msgs = XwJNI.smsproto_prepOutbound( data, phone, forceNow, waitSecs );
|
||||||
if ( count < MAX_MSG_COUNT ) {
|
if ( null != msgs ) {
|
||||||
result = new byte[count][];
|
sendBuffers( msgs, phone );
|
||||||
int msgID = ++s_nSent % 0x000000FF;
|
}
|
||||||
|
if ( waitSecs[0] > 0 ) {
|
||||||
|
Assert.assertFalse( forceNow );
|
||||||
|
postResend( phone, waitSecs[0] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int start = 0;
|
private void postResend( final String phone, final int waitSecs )
|
||||||
int end = 0;
|
{
|
||||||
for ( int ii = 0; ii < count; ++ii ) {
|
Log.d( TAG, "postResend" );
|
||||||
int len = msg.length - end;
|
new Thread(new Runnable() {
|
||||||
if ( len > MAX_LEN_BINARY ) {
|
@Override
|
||||||
len = MAX_LEN_BINARY;
|
public void run() {
|
||||||
}
|
try {
|
||||||
end += len;
|
Thread.sleep( waitSecs * 1000 );
|
||||||
byte[] part = new byte[4 + len];
|
|
||||||
part[0] = (byte)SMS_PROTO_VERSION;
|
|
||||||
part[1] = (byte)msgID;
|
|
||||||
part[2] = (byte)ii;
|
|
||||||
part[3] = (byte)count;
|
|
||||||
System.arraycopy( msg, start, part, 4, len );
|
|
||||||
|
|
||||||
result[ii] = part;
|
Log.d( TAG, "postResend.run()" );
|
||||||
start = end;
|
Intent intent = getIntentTo( SMSService.this,
|
||||||
|
SMSAction.RESEND );
|
||||||
|
intent.putExtra( PHONE, phone );
|
||||||
|
startService( intent );
|
||||||
|
} catch ( InterruptedException ie ) {
|
||||||
|
Log.e( TAG, ie.getMessage() );
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Log.w( TAG, "breakAndEncode(): msg count %d too large; dropping",
|
|
||||||
count );
|
|
||||||
}
|
}
|
||||||
return result;
|
} ).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receive( SMS_CMD cmd, byte[] data, String phone )
|
private void receive( SMS_CMD cmd, byte[] data, String phone )
|
||||||
|
@ -534,53 +531,20 @@ public class SMSService extends XWService {
|
||||||
|
|
||||||
private void receiveBuffer( byte[] buffer, String senderPhone )
|
private void receiveBuffer( byte[] buffer, String senderPhone )
|
||||||
{
|
{
|
||||||
byte proto = buffer[0];
|
byte[][] msgs = XwJNI.smsproto_prepInbound( buffer, senderPhone );
|
||||||
int id = buffer[1];
|
if ( null != msgs ) {
|
||||||
int index = buffer[2];
|
for ( byte[] msg : msgs ) {
|
||||||
int count = buffer[3];
|
if ( !disAssemble( senderPhone, msg ) ) {
|
||||||
byte[] rest = new byte[buffer.length - 4];
|
Log.e( TAG, "failed on message from %s", senderPhone );
|
||||||
System.arraycopy( buffer, 4, rest, 0, rest.length );
|
}
|
||||||
if ( tryAssemble( senderPhone, id, index, count, rest ) ) {
|
}
|
||||||
postEvent( MultiEvent.SMS_RECEIVE_OK );
|
postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||||
} else {
|
} else {
|
||||||
// Will see this when don't have SMS permission
|
Log.w( TAG, "receiveBuffer(): bogus or incomplete message from phone %s",
|
||||||
Log.w( TAG, "receiveBuffer(): bogus message from phone %s",
|
|
||||||
senderPhone );
|
senderPhone );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryAssemble( String senderPhone, int id, int index,
|
|
||||||
int count, byte[] msg )
|
|
||||||
{
|
|
||||||
boolean success = true;
|
|
||||||
if ( index == 0 && count == 1 ) { // most common case
|
|
||||||
success = disAssemble( senderPhone, msg );
|
|
||||||
} else if ( count > 0 && count < MAX_MSG_COUNT && index < count ) {
|
|
||||||
// required? Should always be in main thread.
|
|
||||||
synchronized( s_partialMsgs ) {
|
|
||||||
HashMap<Integer, MsgStore> perPhone =
|
|
||||||
s_partialMsgs.get( senderPhone );
|
|
||||||
if ( null == perPhone ) {
|
|
||||||
perPhone = new HashMap <Integer, MsgStore>();
|
|
||||||
s_partialMsgs.put( senderPhone, perPhone );
|
|
||||||
}
|
|
||||||
MsgStore store = perPhone.get( id );
|
|
||||||
if ( null == store ) {
|
|
||||||
store = new MsgStore( id, count, false );
|
|
||||||
perPhone.put( id, store );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( store.add( index, msg ).isComplete() ) {
|
|
||||||
success = disAssemble( senderPhone, store.messageData() );
|
|
||||||
perPhone.remove( id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean disAssemble( String senderPhone, byte[] fullMsg )
|
private boolean disAssemble( String senderPhone, byte[] fullMsg )
|
||||||
{
|
{
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -685,7 +649,7 @@ public class SMSService extends XWService {
|
||||||
Log.i( TAG, "dropping because SMS disabled" );
|
Log.i( TAG, "dropping because SMS disabled" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( showToasts( this ) && success && (0 == (s_nSent % 5)) ) {
|
if ( showToasts( this ) && success && (0 == (++s_nSent % 5)) ) {
|
||||||
DbgUtils.showf( this, "Sent msg %d", s_nSent );
|
DbgUtils.showf( this, "Sent msg %d", s_nSent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -777,70 +741,4 @@ public class SMSService extends XWService {
|
||||||
return sendPacket( addr.sms_phone, gameID, buf );
|
return sendPacket( addr.sms_phone, gameID, buf );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MsgStore {
|
|
||||||
String[] m_msgsText;
|
|
||||||
byte[][] m_msgsData;
|
|
||||||
int m_msgID;
|
|
||||||
int m_haveCount;
|
|
||||||
int m_fullLength;
|
|
||||||
|
|
||||||
public MsgStore( int id, int count, boolean usingStrings )
|
|
||||||
{
|
|
||||||
m_msgID = id;
|
|
||||||
if ( usingStrings ) {
|
|
||||||
m_msgsText = new String[count];
|
|
||||||
} else {
|
|
||||||
m_msgsData = new byte[count][];
|
|
||||||
}
|
|
||||||
m_fullLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MsgStore add( int index, String msg )
|
|
||||||
{
|
|
||||||
if ( null == m_msgsText[index] ) {
|
|
||||||
++m_haveCount;
|
|
||||||
m_fullLength += msg.length();
|
|
||||||
}
|
|
||||||
m_msgsText[index] = msg;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MsgStore add( int index, byte[] msg )
|
|
||||||
{
|
|
||||||
if ( null == m_msgsData[index] ) {
|
|
||||||
++m_haveCount;
|
|
||||||
m_fullLength += msg.length;
|
|
||||||
}
|
|
||||||
m_msgsData[index] = msg;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isComplete()
|
|
||||||
{
|
|
||||||
int count = null != m_msgsText ? m_msgsText.length : m_msgsData.length;
|
|
||||||
boolean complete = count == m_haveCount;
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String messageText()
|
|
||||||
{
|
|
||||||
StringBuffer sb = new StringBuffer(m_fullLength);
|
|
||||||
for ( String msg : m_msgsText ) {
|
|
||||||
sb.append( msg );
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] messageData()
|
|
||||||
{
|
|
||||||
byte[] result = new byte[m_fullLength];
|
|
||||||
int indx = 0;
|
|
||||||
for ( byte[] msg : m_msgsData ) {
|
|
||||||
System.arraycopy( msg, 0, result, indx, msg.length );
|
|
||||||
indx += msg.length;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,8 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
|
||||||
m_dals = dals;
|
m_dals = dals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected String doInBackground( Void... unused )
|
@Override
|
||||||
|
protected String doInBackground( Void... unused )
|
||||||
{
|
{
|
||||||
HttpURLConnection conn
|
HttpURLConnection conn
|
||||||
= NetUtils.makeHttpUpdateConn( m_context, "getUpdates" );
|
= NetUtils.makeHttpUpdateConn( m_context, "getUpdates" );
|
||||||
|
@ -267,7 +268,8 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onPostExecute( String json )
|
@Override
|
||||||
|
protected void onPostExecute( String json )
|
||||||
{
|
{
|
||||||
if ( null != json ) {
|
if ( null != json ) {
|
||||||
makeNotificationsIf( json, m_params );
|
makeNotificationsIf( json, m_params );
|
||||||
|
|
|
@ -34,17 +34,16 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.text.ClipboardManager;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.media.Ringtone;
|
import android.media.Ringtone;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.ContactsContract.PhoneLookup;
|
import android.provider.ContactsContract.PhoneLookup;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.text.ClipboardManager;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -486,13 +485,16 @@ public class Utils {
|
||||||
return null == s_appVersion? 0 : s_appVersion;
|
return null == s_appVersion? 0 : s_appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Intent makeInstallIntent( File file )
|
public static Intent makeInstallIntent( Context context, File file )
|
||||||
{
|
{
|
||||||
String withScheme = "file://" + file.getPath();
|
Uri uri = FileProvider
|
||||||
Uri uri = Uri.parse( withScheme );
|
.getUriForFile( context,
|
||||||
|
BuildConfig.APPLICATION_ID + ".provider",
|
||||||
|
file );
|
||||||
Intent intent = new Intent( Intent.ACTION_VIEW );
|
Intent intent = new Intent( Intent.ACTION_VIEW );
|
||||||
intent.setDataAndType( uri, XWConstants.APK_TYPE );
|
intent.setDataAndType( uri, XWConstants.APK_TYPE );
|
||||||
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
|
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
| Intent.FLAG_GRANT_READ_URI_PERMISSION );
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +503,7 @@ public class Utils {
|
||||||
{
|
{
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
Intent intent = makeInstallIntent( path );
|
Intent intent = makeInstallIntent( context, path );
|
||||||
List<ResolveInfo> doers =
|
List<ResolveInfo> doers =
|
||||||
pm.queryIntentActivities( intent,
|
pm.queryIntentActivities( intent,
|
||||||
PackageManager.MATCH_DEFAULT_ONLY );
|
PackageManager.MATCH_DEFAULT_ONLY );
|
||||||
|
|
|
@ -82,6 +82,11 @@ public class XWPrefs {
|
||||||
return getPrefsBoolean( context, R.string.key_enable_sms_toself, false );
|
return getPrefsBoolean( context, R.string.key_enable_sms_toself, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean getSMSProtoEnabled( Context context )
|
||||||
|
{
|
||||||
|
return getPrefsBoolean( context, R.string.key_enable_smsproto, false );
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean getHideNewgameButtons( Context context )
|
public static boolean getHideNewgameButtons( Context context )
|
||||||
{
|
{
|
||||||
return getPrefsBoolean( context, R.string.key_hide_newgames,
|
return getPrefsBoolean( context, R.string.key_hide_newgames,
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2009-2010 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eehouse.android.xw4.jni;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.telephony.PhoneNumberUtils;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.eehouse.android.xw4.XWApp;
|
||||||
|
import org.eehouse.android.xw4.DBUtils;
|
||||||
|
import org.eehouse.android.xw4.DevID;
|
||||||
|
import org.eehouse.android.xw4.R;
|
||||||
|
import org.eehouse.android.xw4.Log;
|
||||||
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
|
public class DUtilCtxt {
|
||||||
|
private static final String TAG = DUtilCtxt.class.getSimpleName();
|
||||||
|
|
||||||
|
private Context m_context;
|
||||||
|
|
||||||
|
public DUtilCtxt() {
|
||||||
|
m_context = XWApp.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possible values for typ[0], these must match enum in xwrelay.sh
|
||||||
|
public enum DevIDType { ID_TYPE_NONE
|
||||||
|
, ID_TYPE_RELAY
|
||||||
|
, ID_TYPE_LINUX
|
||||||
|
, ID_TYPE_ANDROID_GCM
|
||||||
|
, ID_TYPE_ANDROID_OTHER
|
||||||
|
, ID_TYPE_ANON
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDevID( /*out*/ byte[] typa )
|
||||||
|
{
|
||||||
|
DevIDType typ = DevIDType.ID_TYPE_NONE;
|
||||||
|
String result = DevID.getRelayDevID( m_context );
|
||||||
|
if ( null != result ) {
|
||||||
|
typ = DevIDType.ID_TYPE_RELAY;
|
||||||
|
} else {
|
||||||
|
result = DevID.getGCMDevID( m_context );
|
||||||
|
if ( result.equals("") ) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
typ = DevIDType.ID_TYPE_ANDROID_GCM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typa[0] = (byte)typ.ordinal();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deviceRegistered( DevIDType devIDType, String idRelay )
|
||||||
|
{
|
||||||
|
switch ( devIDType ) {
|
||||||
|
case ID_TYPE_RELAY:
|
||||||
|
DevID.setRelayDevID( m_context, idRelay );
|
||||||
|
break;
|
||||||
|
case ID_TYPE_NONE:
|
||||||
|
DevID.clearRelayDevID( m_context );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Assert.fail();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int STRD_ROBOT_TRADED = 1;
|
||||||
|
static final int STR_ROBOT_MOVED = 2;
|
||||||
|
static final int STRS_VALUES_HEADER = 3;
|
||||||
|
static final int STRD_REMAINING_TILES_ADD = 4;
|
||||||
|
static final int STRD_UNUSED_TILES_SUB = 5;
|
||||||
|
static final int STRS_REMOTE_MOVED = 6;
|
||||||
|
static final int STRD_TIME_PENALTY_SUB = 7;
|
||||||
|
static final int STR_PASS = 8;
|
||||||
|
static final int STRS_MOVE_ACROSS = 9;
|
||||||
|
static final int STRS_MOVE_DOWN = 10;
|
||||||
|
static final int STRS_TRAY_AT_START = 11;
|
||||||
|
static final int STRSS_TRADED_FOR = 12;
|
||||||
|
static final int STR_PHONY_REJECTED = 13;
|
||||||
|
static final int STRD_CUMULATIVE_SCORE = 14;
|
||||||
|
static final int STRS_NEW_TILES = 15;
|
||||||
|
static final int STR_COMMIT_CONFIRM = 16;
|
||||||
|
static final int STR_BONUS_ALL = 17;
|
||||||
|
static final int STRD_TURN_SCORE = 18;
|
||||||
|
static final int STRD_REMAINS_HEADER = 19;
|
||||||
|
static final int STRD_REMAINS_EXPL = 20;
|
||||||
|
static final int STRSD_RESIGNED = 21;
|
||||||
|
static final int STRSD_WINNER = 22;
|
||||||
|
static final int STRDSD_PLACER = 23;
|
||||||
|
|
||||||
|
|
||||||
|
public String getUserString( int stringCode )
|
||||||
|
{
|
||||||
|
Log.d( TAG, "getUserString(%d)", stringCode );
|
||||||
|
int id = 0;
|
||||||
|
switch( stringCode ) {
|
||||||
|
case STR_ROBOT_MOVED:
|
||||||
|
id = R.string.str_robot_moved_fmt;
|
||||||
|
break;
|
||||||
|
case STRS_VALUES_HEADER:
|
||||||
|
id = R.string.strs_values_header_fmt;
|
||||||
|
break;
|
||||||
|
case STRD_REMAINING_TILES_ADD:
|
||||||
|
id = R.string.strd_remaining_tiles_add_fmt;
|
||||||
|
break;
|
||||||
|
case STRD_UNUSED_TILES_SUB:
|
||||||
|
id = R.string.strd_unused_tiles_sub_fmt;
|
||||||
|
break;
|
||||||
|
case STRS_REMOTE_MOVED:
|
||||||
|
id = R.string.str_remote_moved_fmt;
|
||||||
|
break;
|
||||||
|
case STRD_TIME_PENALTY_SUB:
|
||||||
|
id = R.string.strd_time_penalty_sub_fmt;
|
||||||
|
break;
|
||||||
|
case STR_PASS:
|
||||||
|
id = R.string.str_pass;
|
||||||
|
break;
|
||||||
|
case STRS_MOVE_ACROSS:
|
||||||
|
id = R.string.strs_move_across_fmt;
|
||||||
|
break;
|
||||||
|
case STRS_MOVE_DOWN:
|
||||||
|
id = R.string.strs_move_down_fmt;
|
||||||
|
break;
|
||||||
|
case STRS_TRAY_AT_START:
|
||||||
|
id = R.string.strs_tray_at_start_fmt;
|
||||||
|
break;
|
||||||
|
case STRSS_TRADED_FOR:
|
||||||
|
id = R.string.strss_traded_for_fmt;
|
||||||
|
break;
|
||||||
|
case STR_PHONY_REJECTED:
|
||||||
|
id = R.string.str_phony_rejected;
|
||||||
|
break;
|
||||||
|
case STRD_CUMULATIVE_SCORE:
|
||||||
|
id = R.string.strd_cumulative_score_fmt;
|
||||||
|
break;
|
||||||
|
case STRS_NEW_TILES:
|
||||||
|
id = R.string.strs_new_tiles_fmt;
|
||||||
|
break;
|
||||||
|
case STR_COMMIT_CONFIRM:
|
||||||
|
id = R.string.str_commit_confirm;
|
||||||
|
break;
|
||||||
|
case STR_BONUS_ALL:
|
||||||
|
id = R.string.str_bonus_all;
|
||||||
|
break;
|
||||||
|
case STRD_TURN_SCORE:
|
||||||
|
id = R.string.strd_turn_score_fmt;
|
||||||
|
break;
|
||||||
|
case STRSD_RESIGNED:
|
||||||
|
id = R.string.str_resigned_fmt;
|
||||||
|
break;
|
||||||
|
case STRSD_WINNER:
|
||||||
|
id = R.string.str_winner_fmt;
|
||||||
|
break;
|
||||||
|
case STRDSD_PLACER:
|
||||||
|
id = R.string.str_placer_fmt;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.w( TAG, "no such stringCode: %d", stringCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = (0 == id) ? "" : LocUtils.getString( m_context, id );
|
||||||
|
Log.d( TAG, "getUserString() => %s", result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserQuantityString( int stringCode, int quantity )
|
||||||
|
{
|
||||||
|
int pluralsId = 0;
|
||||||
|
switch ( stringCode ) {
|
||||||
|
case STRD_ROBOT_TRADED:
|
||||||
|
pluralsId = R.plurals.strd_robot_traded_fmt;
|
||||||
|
break;
|
||||||
|
case STRD_REMAINS_HEADER:
|
||||||
|
pluralsId = R.plurals.strd_remains_header_fmt;
|
||||||
|
break;
|
||||||
|
case STRD_REMAINS_EXPL:
|
||||||
|
pluralsId = R.plurals.strd_remains_expl_fmt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
if ( 0 != pluralsId ) {
|
||||||
|
result = LocUtils.getQuantityString( m_context, pluralsId, quantity );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean phoneNumbersSame( String num1, String num2 )
|
||||||
|
{
|
||||||
|
boolean same = PhoneNumberUtils.compare( m_context, num1, num2 );
|
||||||
|
return same;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void store( String key, byte[] data )
|
||||||
|
{
|
||||||
|
Log.d( TAG, "store(key=%s)", key );
|
||||||
|
|
||||||
|
if ( null == data ) {
|
||||||
|
} else {
|
||||||
|
DBUtils.setBytesFor( m_context, key, data );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] load( String key )
|
||||||
|
{
|
||||||
|
byte[] result = null;
|
||||||
|
int resultLen = 0;
|
||||||
|
Log.d( TAG, "load(key=%s)", key );
|
||||||
|
|
||||||
|
result = DBUtils.getBytesFor( m_context, key );
|
||||||
|
if ( result != null ) {
|
||||||
|
resultLen = result.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d( TAG, "load(%s) returning %d bytes", key, resultLen );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -212,7 +212,6 @@ public class JNIThread extends Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
CommonPrefs cp = CommonPrefs.get( context );
|
CommonPrefs cp = CommonPrefs.get( context );
|
||||||
JNIUtils jniUtils = JNIUtilsImpl.get( context );
|
|
||||||
|
|
||||||
// Assert.assertNull( m_jniGamePtr ); // fired!!
|
// Assert.assertNull( m_jniGamePtr ); // fired!!
|
||||||
if ( null != m_jniGamePtr ) {
|
if ( null != m_jniGamePtr ) {
|
||||||
|
@ -224,15 +223,13 @@ public class JNIThread extends Thread {
|
||||||
dictNames, pairs.m_bytes,
|
dictNames, pairs.m_bytes,
|
||||||
pairs.m_paths,
|
pairs.m_paths,
|
||||||
m_gi.langName( m_context ),
|
m_gi.langName( m_context ),
|
||||||
utils, jniUtils,
|
utils, null, cp, m_xport );
|
||||||
null, cp, m_xport );
|
|
||||||
}
|
}
|
||||||
if ( null == m_jniGamePtr ) {
|
if ( null == m_jniGamePtr ) {
|
||||||
m_jniGamePtr = XwJNI.initNew( m_gi, dictNames, pairs.m_bytes,
|
m_jniGamePtr = XwJNI.initNew( m_gi, dictNames, pairs.m_bytes,
|
||||||
pairs.m_paths,
|
pairs.m_paths,
|
||||||
m_gi.langName(m_context),
|
m_gi.langName(m_context),
|
||||||
utils, jniUtils, null, cp,
|
utils, null, cp, m_xport );
|
||||||
m_xport );
|
|
||||||
}
|
}
|
||||||
Assert.assertNotNull( m_jniGamePtr );
|
Assert.assertNotNull( m_jniGamePtr );
|
||||||
m_lastSavedState = Arrays.hashCode( stream );
|
m_lastSavedState = Arrays.hashCode( stream );
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.content.Context;
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.DBUtils;
|
import org.eehouse.android.xw4.DBUtils;
|
||||||
|
import org.eehouse.android.xw4.XWApp;
|
||||||
import org.eehouse.android.xw4.Log;
|
import org.eehouse.android.xw4.Log;
|
||||||
import org.eehouse.android.xw4.Utils;
|
import org.eehouse.android.xw4.Utils;
|
||||||
|
|
||||||
|
@ -41,14 +42,13 @@ public class JNIUtilsImpl implements JNIUtils {
|
||||||
private static JNIUtilsImpl s_impl = null;
|
private static JNIUtilsImpl s_impl = null;
|
||||||
private Context m_context;
|
private Context m_context;
|
||||||
|
|
||||||
private JNIUtilsImpl(){}
|
private JNIUtilsImpl(Context context) { m_context = context; }
|
||||||
|
|
||||||
public static JNIUtils get( Context context )
|
public static synchronized JNIUtils get()
|
||||||
{
|
{
|
||||||
if ( null == s_impl ) {
|
if ( null == s_impl ) {
|
||||||
s_impl = new JNIUtilsImpl();
|
s_impl = new JNIUtilsImpl( XWApp.getContext() );
|
||||||
}
|
}
|
||||||
s_impl.m_context = context;
|
|
||||||
return s_impl;
|
return s_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +133,8 @@ public class JNIUtilsImpl implements JNIUtils {
|
||||||
|
|
||||||
public String getMD5SumFor( byte[] bytes )
|
public String getMD5SumFor( byte[] bytes )
|
||||||
{
|
{
|
||||||
|
String result = null;
|
||||||
|
if ( bytes != null ) {
|
||||||
byte[] digest = null;
|
byte[] digest = null;
|
||||||
try {
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
@ -150,7 +152,9 @@ public class JNIUtilsImpl implements JNIUtils {
|
||||||
} catch ( java.security.NoSuchAlgorithmException nsae ) {
|
} catch ( java.security.NoSuchAlgorithmException nsae ) {
|
||||||
Log.ex( TAG, nsae );
|
Log.ex( TAG, nsae );
|
||||||
}
|
}
|
||||||
return Utils.digestToString( digest );
|
result = Utils.digestToString( digest );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMD5SumFor( String dictName, byte[] bytes )
|
public String getMD5SumFor( String dictName, byte[] bytes )
|
||||||
|
|
|
@ -60,50 +60,10 @@ public interface UtilCtxt {
|
||||||
void remSelected();
|
void remSelected();
|
||||||
void setIsServer( boolean isServer );
|
void setIsServer( boolean isServer );
|
||||||
|
|
||||||
// Possible values for typ[0], these must match enum in xwrelay.sh
|
|
||||||
public enum DevIDType { ID_TYPE_NONE
|
|
||||||
, ID_TYPE_RELAY
|
|
||||||
, ID_TYPE_LINUX
|
|
||||||
, ID_TYPE_ANDROID_GCM
|
|
||||||
, ID_TYPE_ANDROID_OTHER
|
|
||||||
, ID_TYPE_ANON
|
|
||||||
}
|
|
||||||
|
|
||||||
String getDevID( /*out*/ byte[] typ );
|
|
||||||
void deviceRegistered( DevIDType devIDType, String idRelay );
|
|
||||||
|
|
||||||
void bonusSquareHeld( int bonus );
|
void bonusSquareHeld( int bonus );
|
||||||
void playerScoreHeld( int player );
|
void playerScoreHeld( int player );
|
||||||
void cellSquareHeld( String words );
|
void cellSquareHeld( String words );
|
||||||
|
|
||||||
static final int STRD_ROBOT_TRADED = 1;
|
|
||||||
static final int STR_ROBOT_MOVED = 2;
|
|
||||||
static final int STRS_VALUES_HEADER = 3;
|
|
||||||
static final int STRD_REMAINING_TILES_ADD = 4;
|
|
||||||
static final int STRD_UNUSED_TILES_SUB = 5;
|
|
||||||
static final int STRS_REMOTE_MOVED = 6;
|
|
||||||
static final int STRD_TIME_PENALTY_SUB = 7;
|
|
||||||
static final int STR_PASS = 8;
|
|
||||||
static final int STRS_MOVE_ACROSS = 9;
|
|
||||||
static final int STRS_MOVE_DOWN = 10;
|
|
||||||
static final int STRS_TRAY_AT_START = 11;
|
|
||||||
static final int STRSS_TRADED_FOR = 12;
|
|
||||||
static final int STR_PHONY_REJECTED = 13;
|
|
||||||
static final int STRD_CUMULATIVE_SCORE = 14;
|
|
||||||
static final int STRS_NEW_TILES = 15;
|
|
||||||
static final int STR_COMMIT_CONFIRM = 16;
|
|
||||||
static final int STR_BONUS_ALL = 17;
|
|
||||||
static final int STRD_TURN_SCORE = 18;
|
|
||||||
static final int STRD_REMAINS_HEADER = 19;
|
|
||||||
static final int STRD_REMAINS_EXPL = 20;
|
|
||||||
static final int STRSD_RESIGNED = 21;
|
|
||||||
static final int STRSD_WINNER = 22;
|
|
||||||
static final int STRDSD_PLACER = 23;
|
|
||||||
|
|
||||||
|
|
||||||
String getUserString( int stringCode );
|
|
||||||
String getUserQuantityString( int stringCode, int quantity );
|
|
||||||
|
|
||||||
void notifyMove( String query );
|
void notifyMove( String query );
|
||||||
void notifyTrade( String[] tiles );
|
void notifyTrade( String[] tiles );
|
||||||
|
|
||||||
|
@ -146,6 +106,4 @@ public interface UtilCtxt {
|
||||||
boolean turnLost );
|
boolean turnLost );
|
||||||
|
|
||||||
void showChat( String msg, int fromIndx, String fromName, int tsSeconds );
|
void showChat( String msg, int fromIndx, String fromName, int tsSeconds );
|
||||||
|
|
||||||
boolean phoneNumbersSame( String num1, String num2 );
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,11 @@
|
||||||
package org.eehouse.android.xw4.jni;
|
package org.eehouse.android.xw4.jni;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.telephony.PhoneNumberUtils;
|
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.DevID;
|
|
||||||
import org.eehouse.android.xw4.Log;
|
import org.eehouse.android.xw4.Log;
|
||||||
import org.eehouse.android.xw4.R;
|
|
||||||
import org.eehouse.android.xw4.XWApp;
|
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
|
||||||
|
|
||||||
public class UtilCtxtImpl implements UtilCtxt {
|
public class UtilCtxtImpl implements UtilCtxt {
|
||||||
private static final String TAG = UtilCtxtImpl.class.getSimpleName();
|
private static final String TAG = UtilCtxtImpl.class.getSimpleName();
|
||||||
|
@ -44,222 +39,114 @@ public class UtilCtxtImpl implements UtilCtxt {
|
||||||
m_context = context;
|
m_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void requestTime() {
|
public void requestTime() {
|
||||||
subclassOverride( "requestTime" );
|
subclassOverride( "requestTime" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notifyPickTileBlank( int playerNum, int col, int row,
|
public void notifyPickTileBlank( int playerNum, int col, int row,
|
||||||
String[] texts )
|
String[] texts )
|
||||||
{
|
{
|
||||||
subclassOverride( "userPickTileBlank" );
|
subclassOverride( "userPickTileBlank" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
|
public void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
|
||||||
String[] texts, int[] counts )
|
String[] texts, int[] counts )
|
||||||
{
|
{
|
||||||
subclassOverride( "informNeedPickTiles" );
|
subclassOverride( "informNeedPickTiles" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informNeedPassword( int player, String name )
|
public void informNeedPassword( int player, String name )
|
||||||
{
|
{
|
||||||
subclassOverride( "informNeedPassword" );
|
subclassOverride( "informNeedPassword" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void turnChanged( int newTurn )
|
public void turnChanged( int newTurn )
|
||||||
{
|
{
|
||||||
subclassOverride( "turnChanged" );
|
subclassOverride( "turnChanged" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean engineProgressCallback()
|
public boolean engineProgressCallback()
|
||||||
{
|
{
|
||||||
// subclassOverride( "engineProgressCallback" );
|
// subclassOverride( "engineProgressCallback" );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setTimer( int why, int when, int handle )
|
public void setTimer( int why, int when, int handle )
|
||||||
{
|
{
|
||||||
subclassOverride( "setTimer" );
|
subclassOverride( "setTimer" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void clearTimer( int why )
|
public void clearTimer( int why )
|
||||||
{
|
{
|
||||||
subclassOverride( "clearTimer" );
|
subclassOverride( "clearTimer" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void remSelected()
|
public void remSelected()
|
||||||
{
|
{
|
||||||
subclassOverride( "remSelected" );
|
subclassOverride( "remSelected" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setIsServer( boolean isServer )
|
public void setIsServer( boolean isServer )
|
||||||
{
|
{
|
||||||
subclassOverride( "setIsServer" );
|
subclassOverride( "setIsServer" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDevID( /*out*/ byte[] typa )
|
@Override
|
||||||
{
|
|
||||||
UtilCtxt.DevIDType typ = UtilCtxt.DevIDType.ID_TYPE_NONE;
|
|
||||||
String result = DevID.getRelayDevID( m_context );
|
|
||||||
if ( null != result ) {
|
|
||||||
typ = UtilCtxt.DevIDType.ID_TYPE_RELAY;
|
|
||||||
} else {
|
|
||||||
result = DevID.getGCMDevID( m_context );
|
|
||||||
if ( result.equals("") ) {
|
|
||||||
result = null;
|
|
||||||
} else {
|
|
||||||
typ = UtilCtxt.DevIDType.ID_TYPE_ANDROID_GCM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typa[0] = (byte)typ.ordinal();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deviceRegistered( UtilCtxt.DevIDType devIDType, String idRelay )
|
|
||||||
{
|
|
||||||
switch ( devIDType ) {
|
|
||||||
case ID_TYPE_RELAY:
|
|
||||||
DevID.setRelayDevID( m_context, idRelay );
|
|
||||||
break;
|
|
||||||
case ID_TYPE_NONE:
|
|
||||||
DevID.clearRelayDevID( m_context );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Assert.fail();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bonusSquareHeld( int bonus )
|
public void bonusSquareHeld( int bonus )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void playerScoreHeld( int player )
|
public void playerScoreHeld( int player )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void cellSquareHeld( String words )
|
public void cellSquareHeld( String words )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserString( int stringCode )
|
@Override
|
||||||
{
|
|
||||||
int id = 0;
|
|
||||||
switch( stringCode ) {
|
|
||||||
case UtilCtxt.STR_ROBOT_MOVED:
|
|
||||||
id = R.string.str_robot_moved_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_VALUES_HEADER:
|
|
||||||
id = R.string.strs_values_header_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_REMAINING_TILES_ADD:
|
|
||||||
id = R.string.strd_remaining_tiles_add_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_UNUSED_TILES_SUB:
|
|
||||||
id = R.string.strd_unused_tiles_sub_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_REMOTE_MOVED:
|
|
||||||
id = R.string.str_remote_moved_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_TIME_PENALTY_SUB:
|
|
||||||
id = R.string.strd_time_penalty_sub_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STR_PASS:
|
|
||||||
id = R.string.str_pass;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_MOVE_ACROSS:
|
|
||||||
id = R.string.strs_move_across_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_MOVE_DOWN:
|
|
||||||
id = R.string.strs_move_down_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_TRAY_AT_START:
|
|
||||||
id = R.string.strs_tray_at_start_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRSS_TRADED_FOR:
|
|
||||||
id = R.string.strss_traded_for_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STR_PHONY_REJECTED:
|
|
||||||
id = R.string.str_phony_rejected;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_CUMULATIVE_SCORE:
|
|
||||||
id = R.string.strd_cumulative_score_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRS_NEW_TILES:
|
|
||||||
id = R.string.strs_new_tiles_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STR_COMMIT_CONFIRM:
|
|
||||||
id = R.string.str_commit_confirm;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STR_BONUS_ALL:
|
|
||||||
id = R.string.str_bonus_all;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_TURN_SCORE:
|
|
||||||
id = R.string.strd_turn_score_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRSD_RESIGNED:
|
|
||||||
id = R.string.str_resigned_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRSD_WINNER:
|
|
||||||
id = R.string.str_winner_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRDSD_PLACER:
|
|
||||||
id = R.string.str_placer_fmt;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log.w( TAG, "no such stringCode: %d", stringCode );
|
|
||||||
}
|
|
||||||
|
|
||||||
String result = (0 == id) ? "" : LocUtils.getString( m_context, id );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserQuantityString( int stringCode, int quantity )
|
|
||||||
{
|
|
||||||
int pluralsId = 0;
|
|
||||||
switch ( stringCode ) {
|
|
||||||
case UtilCtxt.STRD_ROBOT_TRADED:
|
|
||||||
pluralsId = R.plurals.strd_robot_traded_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_REMAINS_HEADER:
|
|
||||||
pluralsId = R.plurals.strd_remains_header_fmt;
|
|
||||||
break;
|
|
||||||
case UtilCtxt.STRD_REMAINS_EXPL:
|
|
||||||
pluralsId = R.plurals.strd_remains_expl_fmt;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String result = "";
|
|
||||||
if ( 0 != pluralsId ) {
|
|
||||||
result = LocUtils.getQuantityString( m_context, pluralsId, quantity );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyMove( String query )
|
public void notifyMove( String query )
|
||||||
{
|
{
|
||||||
subclassOverride( "notifyMove" );
|
subclassOverride( "notifyMove" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notifyTrade( String[] tiles )
|
public void notifyTrade( String[] tiles )
|
||||||
{
|
{
|
||||||
subclassOverride( "notifyTrade" );
|
subclassOverride( "notifyTrade" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void userError( int id )
|
public void userError( int id )
|
||||||
{
|
{
|
||||||
subclassOverride( "userError" );
|
subclassOverride( "userError" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informMove( int turn, String expl, String words )
|
public void informMove( int turn, String expl, String words )
|
||||||
{
|
{
|
||||||
subclassOverride( "informMove" );
|
subclassOverride( "informMove" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informUndo()
|
public void informUndo()
|
||||||
{
|
{
|
||||||
subclassOverride( "informUndo" );
|
subclassOverride( "informUndo" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informNetDict( int lang, String oldName,
|
public void informNetDict( int lang, String oldName,
|
||||||
String newName, String newSum,
|
String newName, String newSum,
|
||||||
CurGameInfo.XWPhoniesChoice phonies )
|
CurGameInfo.XWPhoniesChoice phonies )
|
||||||
|
@ -267,6 +154,7 @@ public class UtilCtxtImpl implements UtilCtxt {
|
||||||
subclassOverride( "informNetDict" );
|
subclassOverride( "informNetDict" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void informMissing( boolean isServer,
|
public void informMissing( boolean isServer,
|
||||||
CommsConnTypeSet connTypes,
|
CommsConnTypeSet connTypes,
|
||||||
int nDevices, int nMissingPlayers )
|
int nDevices, int nMissingPlayers )
|
||||||
|
@ -276,11 +164,13 @@ public class UtilCtxtImpl implements UtilCtxt {
|
||||||
|
|
||||||
// Probably want to cache the fact that the game over notification
|
// Probably want to cache the fact that the game over notification
|
||||||
// showed up and then display it next time game's opened.
|
// showed up and then display it next time game's opened.
|
||||||
|
@Override
|
||||||
public void notifyGameOver()
|
public void notifyGameOver()
|
||||||
{
|
{
|
||||||
subclassOverride( "notifyGameOver" );
|
subclassOverride( "notifyGameOver" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notifyIllegalWords( String dict, String[] words, int turn,
|
public void notifyIllegalWords( String dict, String[] words, int turn,
|
||||||
boolean turnLost )
|
boolean turnLost )
|
||||||
{
|
{
|
||||||
|
@ -288,17 +178,12 @@ public class UtilCtxtImpl implements UtilCtxt {
|
||||||
}
|
}
|
||||||
|
|
||||||
// These need to go into some sort of chat DB, not dropped.
|
// These need to go into some sort of chat DB, not dropped.
|
||||||
|
@Override
|
||||||
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
||||||
{
|
{
|
||||||
subclassOverride( "showChat" );
|
subclassOverride( "showChat" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean phoneNumbersSame( String num1, String num2 )
|
|
||||||
{
|
|
||||||
boolean same = PhoneNumberUtils.compare( m_context, num1, num2 );
|
|
||||||
return same;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void subclassOverride( String name ) {
|
private void subclassOverride( String name ) {
|
||||||
// DbgUtils.logf( "%s::%s() called", getClass().getName(), name );
|
// DbgUtils.logf( "%s::%s() called", getClass().getName(), name );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
|
* Copyright 2009 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
* rights reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License as
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
@ -97,7 +97,7 @@ public class XwJNI {
|
||||||
private int m_ptr;
|
private int m_ptr;
|
||||||
private XwJNI()
|
private XwJNI()
|
||||||
{
|
{
|
||||||
m_ptr = initGlobals();
|
m_ptr = initGlobals( new DUtilCtxt(), JNIUtilsImpl.get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanGlobals()
|
public static void cleanGlobals()
|
||||||
|
@ -134,23 +134,30 @@ public class XwJNI {
|
||||||
public static native boolean timerFired( GamePtr gamePtr, int why,
|
public static native boolean timerFired( GamePtr gamePtr, int why,
|
||||||
int when, int handle );
|
int when, int handle );
|
||||||
|
|
||||||
// Stateless methods
|
public static byte[] gi_to_stream( CurGameInfo gi )
|
||||||
public static native byte[] gi_to_stream( CurGameInfo gi );
|
{
|
||||||
public static native void gi_from_stream( CurGameInfo gi, byte[] stream );
|
return gi_to_stream( getJNI().m_ptr, gi );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void gi_from_stream( CurGameInfo gi, byte[] stream )
|
||||||
|
{
|
||||||
|
gi_from_stream( getJNI().m_ptr, gi, stream );
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] nliToStream( NetLaunchInfo nli )
|
public static byte[] nliToStream( NetLaunchInfo nli )
|
||||||
{
|
{
|
||||||
nli.freezeAddrs();
|
nli.freezeAddrs();
|
||||||
return nli_to_stream( nli );
|
return nli_to_stream( getJNI().m_ptr, nli );
|
||||||
}
|
}
|
||||||
private static native byte[] nli_to_stream( NetLaunchInfo nli );
|
|
||||||
public static NetLaunchInfo nliFromStream( byte[] stream )
|
public static NetLaunchInfo nliFromStream( byte[] stream )
|
||||||
{
|
{
|
||||||
NetLaunchInfo nli = new NetLaunchInfo();
|
NetLaunchInfo nli = new NetLaunchInfo();
|
||||||
nli_from_stream( nli, stream );
|
nli_from_stream( getJNI().m_ptr, nli, stream );
|
||||||
nli.unfreezeAddrs();
|
nli.unfreezeAddrs();
|
||||||
return nli;
|
return nli;
|
||||||
}
|
}
|
||||||
private static native void nli_from_stream( NetLaunchInfo nli, byte[] stream );
|
|
||||||
public static native void comms_getInitialAddr( CommsAddrRec addr,
|
public static native void comms_getInitialAddr( CommsAddrRec addr,
|
||||||
String relayHost,
|
String relayHost,
|
||||||
int relayPort );
|
int relayPort );
|
||||||
|
@ -160,8 +167,7 @@ public class XwJNI {
|
||||||
private static GamePtr initJNI( long rowid )
|
private static GamePtr initJNI( long rowid )
|
||||||
{
|
{
|
||||||
int seed = Utils.nextRandomInt();
|
int seed = Utils.nextRandomInt();
|
||||||
String tag = String.format( "%d", rowid );
|
int ptr = initJNI( getJNI().m_ptr, seed );
|
||||||
int ptr = initJNI( getJNI().m_ptr, seed, tag );
|
|
||||||
GamePtr result = 0 == ptr ? null : new GamePtr( ptr, rowid );
|
GamePtr result = 0 == ptr ? null : new GamePtr( ptr, rowid );
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -170,13 +176,13 @@ public class XwJNI {
|
||||||
initFromStream( long rowid, byte[] stream, CurGameInfo gi,
|
initFromStream( long rowid, byte[] stream, CurGameInfo gi,
|
||||||
String[] dictNames, byte[][] dictBytes,
|
String[] dictNames, byte[][] dictBytes,
|
||||||
String[] dictPaths, String langName,
|
String[] dictPaths, String langName,
|
||||||
UtilCtxt util, JNIUtils jniu, DrawCtx draw,
|
UtilCtxt util, DrawCtx draw,
|
||||||
CommonPrefs cp, TransportProcs procs )
|
CommonPrefs cp, TransportProcs procs )
|
||||||
|
|
||||||
{
|
{
|
||||||
GamePtr gamePtr = initJNI( rowid );
|
GamePtr gamePtr = initJNI( rowid );
|
||||||
if ( game_makeFromStream( gamePtr, stream, gi, dictNames, dictBytes,
|
if ( game_makeFromStream( gamePtr, stream, gi, dictNames, dictBytes,
|
||||||
dictPaths, langName, util, jniu, draw,
|
dictPaths, langName, util, draw,
|
||||||
cp, procs ) ) {
|
cp, procs ) ) {
|
||||||
gamePtr.retain();
|
gamePtr.retain();
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,12 +195,11 @@ public class XwJNI {
|
||||||
public static synchronized GamePtr
|
public static synchronized GamePtr
|
||||||
initNew( CurGameInfo gi, String[] dictNames, byte[][] dictBytes,
|
initNew( CurGameInfo gi, String[] dictNames, byte[][] dictBytes,
|
||||||
String[] dictPaths, String langName, UtilCtxt util,
|
String[] dictPaths, String langName, UtilCtxt util,
|
||||||
JNIUtils jniu, DrawCtx draw, CommonPrefs cp,
|
DrawCtx draw, CommonPrefs cp, TransportProcs procs )
|
||||||
TransportProcs procs )
|
|
||||||
{
|
{
|
||||||
GamePtr gamePtr = initJNI( 0 );
|
GamePtr gamePtr = initJNI( 0 );
|
||||||
game_makeNewGame( gamePtr, gi, dictNames, dictBytes, dictPaths,
|
game_makeNewGame( gamePtr, gi, dictNames, dictBytes, dictPaths,
|
||||||
langName, util, jniu, draw, cp, procs );
|
langName, util, draw, cp, procs );
|
||||||
return gamePtr.retain();
|
return gamePtr.retain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +216,6 @@ public class XwJNI {
|
||||||
String[] dictPaths,
|
String[] dictPaths,
|
||||||
String langName,
|
String langName,
|
||||||
UtilCtxt util,
|
UtilCtxt util,
|
||||||
JNIUtils jniu,
|
|
||||||
DrawCtx draw, CommonPrefs cp,
|
DrawCtx draw, CommonPrefs cp,
|
||||||
TransportProcs procs );
|
TransportProcs procs );
|
||||||
|
|
||||||
|
@ -223,7 +227,6 @@ public class XwJNI {
|
||||||
String[] dictPaths,
|
String[] dictPaths,
|
||||||
String langName,
|
String langName,
|
||||||
UtilCtxt util,
|
UtilCtxt util,
|
||||||
JNIUtils jniu,
|
|
||||||
DrawCtx draw,
|
DrawCtx draw,
|
||||||
CommonPrefs cp,
|
CommonPrefs cp,
|
||||||
TransportProcs procs );
|
TransportProcs procs );
|
||||||
|
@ -400,6 +403,19 @@ public class XwJNI {
|
||||||
public static native boolean comms_getAddrDisabled( GamePtr gamePtr, CommsConnType typ,
|
public static native boolean comms_getAddrDisabled( GamePtr gamePtr, CommsConnType typ,
|
||||||
boolean send );
|
boolean send );
|
||||||
|
|
||||||
|
public static byte[][] smsproto_prepOutbound( byte[] buf, String phone, boolean forceNow,
|
||||||
|
/*out*/ int[] waitSecs )
|
||||||
|
{
|
||||||
|
int nowSeconds = (int)(System.currentTimeMillis() / 1000);
|
||||||
|
return smsproto_prepOutbound( getJNI().m_ptr, buf, phone, nowSeconds,
|
||||||
|
forceNow, waitSecs );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[][] smsproto_prepInbound( byte[] data, String fromPhone )
|
||||||
|
{
|
||||||
|
return smsproto_prepInbound( getJNI().m_ptr, data, fromPhone );
|
||||||
|
}
|
||||||
|
|
||||||
// Dicts
|
// Dicts
|
||||||
public static class DictWrapper {
|
public static class DictWrapper {
|
||||||
private int m_dictPtr;
|
private int m_dictPtr;
|
||||||
|
@ -439,12 +455,10 @@ public class XwJNI {
|
||||||
|
|
||||||
public static native boolean dict_tilesAreSame( int dict1, int dict2 );
|
public static native boolean dict_tilesAreSame( int dict1, int dict2 );
|
||||||
public static native String[] dict_getChars( int dict );
|
public static native String[] dict_getChars( int dict );
|
||||||
public static boolean dict_getInfo( byte[] dict, String name,
|
public static boolean dict_getInfo( byte[] dict, String name, String path,
|
||||||
String path, JNIUtils jniu,
|
|
||||||
boolean check, DictInfo info )
|
boolean check, DictInfo info )
|
||||||
{
|
{
|
||||||
return dict_getInfo( getJNI().m_ptr, dict, name, path, jniu,
|
return dict_getInfo( getJNI().m_ptr, dict, name, path, check, info );
|
||||||
check, info );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static native int dict_getTileValue( int dictPtr, int tile );
|
public static native int dict_getTileValue( int dictPtr, int tile );
|
||||||
|
@ -452,9 +466,9 @@ public class XwJNI {
|
||||||
// Dict iterator
|
// Dict iterator
|
||||||
public final static int MAX_COLS_DICT = 15; // from dictiter.h
|
public final static int MAX_COLS_DICT = 15; // from dictiter.h
|
||||||
public static int dict_iter_init( byte[] dict, String name,
|
public static int dict_iter_init( byte[] dict, String name,
|
||||||
String path, JNIUtils jniu )
|
String path )
|
||||||
{
|
{
|
||||||
return dict_iter_init( getJNI().m_ptr, dict, name, path, jniu );
|
return dict_iter_init( getJNI().m_ptr, dict, name, path );
|
||||||
}
|
}
|
||||||
public static native void dict_iter_setMinMax( int closure,
|
public static native void dict_iter_setMinMax( int closure,
|
||||||
int min, int max );
|
int min, int max );
|
||||||
|
@ -469,19 +483,32 @@ public class XwJNI {
|
||||||
public static native String dict_iter_getDesc( int closure );
|
public static native String dict_iter_getDesc( int closure );
|
||||||
|
|
||||||
// Private methods -- called only here
|
// Private methods -- called only here
|
||||||
private static native int initGlobals();
|
private static native int initGlobals(DUtilCtxt dutil, JNIUtils jniu);
|
||||||
private static native void cleanGlobals( int globals );
|
private static native void cleanGlobals( int jniState );
|
||||||
private static native int initJNI( int jniState, int seed, String tag );
|
private static native byte[] gi_to_stream( int jniState, CurGameInfo gi );
|
||||||
|
private static native void gi_from_stream( int jniState, CurGameInfo gi,
|
||||||
|
byte[] stream );
|
||||||
|
private static native byte[] nli_to_stream( int jniState, NetLaunchInfo nli );
|
||||||
|
private static native void nli_from_stream( int jniState, NetLaunchInfo nli,
|
||||||
|
byte[] stream );
|
||||||
|
private static native int initJNI( int jniState, int seed );
|
||||||
private static native void envDone( int globals );
|
private static native void envDone( int globals );
|
||||||
private static native void dict_ref( int dictPtr );
|
private static native void dict_ref( int dictPtr );
|
||||||
private static native void dict_unref( int dictPtr );
|
private static native void dict_unref( int dictPtr );
|
||||||
private static native boolean dict_getInfo( int jniState, byte[] dict,
|
private static native boolean dict_getInfo( int jniState, byte[] dict,
|
||||||
String name, String path,
|
String name, String path,
|
||||||
JNIUtils jniu, boolean check,
|
boolean check,
|
||||||
DictInfo info );
|
DictInfo info );
|
||||||
private static native int dict_iter_init( int jniState, byte[] dict,
|
private static native int dict_iter_init( int jniState, byte[] dict,
|
||||||
String name, String path,
|
String name, String path );
|
||||||
JNIUtils jniu );
|
|
||||||
|
private static native byte[][] smsproto_prepOutbound( int jniState, byte[] buf,
|
||||||
|
String phone, int nowSeconds,
|
||||||
|
boolean forceNow,
|
||||||
|
/*out*/int[] waitSecs );
|
||||||
|
|
||||||
|
private static native byte[][] smsproto_prepInbound( int jniState, byte[] data,
|
||||||
|
String fromPhone );
|
||||||
|
|
||||||
private static native boolean haveEnv( int jniState );
|
private static native boolean haveEnv( int jniState );
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
<string name="key_enable_dup_invite">key_enable_dup_invite</string>
|
<string name="key_enable_dup_invite">key_enable_dup_invite</string>
|
||||||
<string name="key_enable_nfc_toself">key_enable_nfc_toself</string>
|
<string name="key_enable_nfc_toself">key_enable_nfc_toself</string>
|
||||||
<string name="key_enable_sms_toself">key_enable_sms_toself</string>
|
<string name="key_enable_sms_toself">key_enable_sms_toself</string>
|
||||||
|
<string name="key_enable_smsproto">key_enable_smsproto</string>
|
||||||
<string name="key_ignore_gcm">key_ignore_gcm</string>
|
<string name="key_ignore_gcm">key_ignore_gcm</string>
|
||||||
<string name="key_show_gcm">key_show_gcm</string>
|
<string name="key_show_gcm">key_show_gcm</string>
|
||||||
<string name="key_nag_intervals">key_nag_intervals</string>
|
<string name="key_nag_intervals">key_nag_intervals</string>
|
||||||
|
|
|
@ -2516,6 +2516,10 @@
|
||||||
<string name="nag_intervals">Reminder intervals (minutes1,minutes2,...)</string>
|
<string name="nag_intervals">Reminder intervals (minutes1,minutes2,...)</string>
|
||||||
<string name="enable_nfc_toself_title">Enable NFC to self</string>
|
<string name="enable_nfc_toself_title">Enable NFC to self</string>
|
||||||
<string name="enable_nfc_toself_summary">Fake invitation to aid debugging</string>
|
<string name="enable_nfc_toself_summary">Fake invitation to aid debugging</string>
|
||||||
|
|
||||||
|
<string name="enable_smsproto_title">Use new/experimental SMS code</string>
|
||||||
|
<string name="enable_smsproto_summary">(Requires that opponent be using it too)</string>
|
||||||
|
|
||||||
<string name="enable_sms_toself_title">Short-circuit SMS to self</string>
|
<string name="enable_sms_toself_title">Short-circuit SMS to self</string>
|
||||||
<string name="enable_sms_toself_summary">Skip radio when phone numbers same</string>
|
<string name="enable_sms_toself_summary">Skip radio when phone numbers same</string>
|
||||||
|
|
||||||
|
|
4
xwords4/android/app/src/main/res/xml/provider_paths.xml
Normal file
4
xwords4/android/app/src/main/res/xml/provider_paths.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-path name="external_files" path="."/>
|
||||||
|
</paths>
|
|
@ -437,6 +437,11 @@
|
||||||
<PreferenceScreen android:title="@string/pref_group_sms_title"
|
<PreferenceScreen android:title="@string/pref_group_sms_title"
|
||||||
android:summary="@string/pref_group_sms_summary"
|
android:summary="@string/pref_group_sms_summary"
|
||||||
>
|
>
|
||||||
|
<CheckBoxPreference android:key="@string/key_enable_smsproto"
|
||||||
|
android:title="@string/enable_smsproto_title"
|
||||||
|
android:summary="@string/enable_smsproto_summary"
|
||||||
|
android:defaultValue="false"
|
||||||
|
/>
|
||||||
<CheckBoxPreference android:key="@string/key_enable_sms_toself"
|
<CheckBoxPreference android:key="@string/key_enable_sms_toself"
|
||||||
android:title="@string/enable_sms_toself_title"
|
android:title="@string/enable_sms_toself_title"
|
||||||
android:summary="@string/enable_sms_toself_summary"
|
android:summary="@string/enable_sms_toself_summary"
|
||||||
|
|
|
@ -84,6 +84,7 @@ COMMON_SRC_FILES += \
|
||||||
$(COMMON_PATH)/movestak.c \
|
$(COMMON_PATH)/movestak.c \
|
||||||
$(COMMON_PATH)/dbgutil.c \
|
$(COMMON_PATH)/dbgutil.c \
|
||||||
$(COMMON_PATH)/nli.c \
|
$(COMMON_PATH)/nli.c \
|
||||||
|
$(COMMON_PATH)/smsproto.c \
|
||||||
|
|
||||||
LOCAL_CFLAGS+=$(LOCAL_C_INCLUDES) $(LOCAL_DEFINES) -Wall -std=c99
|
LOCAL_CFLAGS+=$(LOCAL_C_INCLUDES) $(LOCAL_DEFINES) -Wall -std=c99
|
||||||
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(LOCAL_SRC_FILES) $(COMMON_SRC_FILES)
|
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(LOCAL_SRC_FILES) $(COMMON_SRC_FILES)
|
||||||
|
|
|
@ -26,15 +26,16 @@
|
||||||
|
|
||||||
typedef struct _JNIState JNIState;
|
typedef struct _JNIState JNIState;
|
||||||
|
|
||||||
typedef struct _AndGlobals {
|
typedef struct _AndGameGlobals {
|
||||||
VTableMgr* vtMgr;
|
VTableMgr* vtMgr;
|
||||||
CurGameInfo* gi;
|
CurGameInfo* gi;
|
||||||
DrawCtx* dctx;
|
DrawCtx* dctx;
|
||||||
XW_UtilCtxt* util;
|
XW_UtilCtxt* util;
|
||||||
struct JNIUtilCtxt* jniutil;
|
struct JNIUtilCtxt* jniutil;
|
||||||
TransportProcs* xportProcs;
|
TransportProcs* xportProcs;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
JNIState* state;
|
JNIState* state;
|
||||||
} AndGlobals;
|
} AndGameGlobals;
|
||||||
|
|
||||||
typedef struct _EnvThreadInfo EnvThreadInfo;
|
typedef struct _EnvThreadInfo EnvThreadInfo;
|
||||||
|
|
||||||
|
|
|
@ -689,7 +689,7 @@ jEnumToInt( JNIEnv* env, jobject jenum )
|
||||||
}
|
}
|
||||||
|
|
||||||
XWStreamCtxt*
|
XWStreamCtxt*
|
||||||
and_empty_stream( MPFORMAL AndGlobals* globals )
|
and_empty_stream( MPFORMAL AndGameGlobals* globals )
|
||||||
{
|
{
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
|
||||||
globals, 0, NULL );
|
globals, 0, NULL );
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
/* callback for streams */
|
/* callback for streams */
|
||||||
void and_send_on_close( XWStreamCtxt* stream, void* closure );
|
void and_send_on_close( XWStreamCtxt* stream, void* closure );
|
||||||
XWStreamCtxt* and_empty_stream( MPFORMAL AndGlobals* globals );
|
XWStreamCtxt* and_empty_stream( MPFORMAL AndGameGlobals* globals );
|
||||||
|
|
||||||
typedef struct _SetInfo {
|
typedef struct _SetInfo {
|
||||||
const char* name;
|
const char* name;
|
||||||
|
|
|
@ -30,6 +30,18 @@
|
||||||
|
|
||||||
#define MAX_QUANTITY_STRS 4
|
#define MAX_QUANTITY_STRS 4
|
||||||
|
|
||||||
|
typedef struct _AndDUtil {
|
||||||
|
XW_DUtilCtxt dutil;
|
||||||
|
EnvThreadInfo* ti;
|
||||||
|
JNIUtilCtxt* jniutil;
|
||||||
|
jobject jdutil; /* global ref to object implementing XW_DUtilCtxt */
|
||||||
|
XP_UCHAR* userStrings[N_AND_USER_STRINGS];
|
||||||
|
XP_U32 userStringsBits;
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
XP_UCHAR* devIDStorage;
|
||||||
|
#endif
|
||||||
|
} AndDUtil;
|
||||||
|
|
||||||
typedef struct _TimerStorage {
|
typedef struct _TimerStorage {
|
||||||
XWTimerProc proc;
|
XWTimerProc proc;
|
||||||
void* closure;
|
void* closure;
|
||||||
|
@ -40,21 +52,8 @@ typedef struct _AndUtil {
|
||||||
EnvThreadInfo* ti;
|
EnvThreadInfo* ti;
|
||||||
jobject jutil; /* global ref to object implementing XW_UtilCtxt */
|
jobject jutil; /* global ref to object implementing XW_UtilCtxt */
|
||||||
TimerStorage timerStorage[NUM_TIMERS_PLUS_ONE];
|
TimerStorage timerStorage[NUM_TIMERS_PLUS_ONE];
|
||||||
XP_UCHAR* userStrings[N_AND_USER_STRINGS];
|
|
||||||
XP_U32 userStringsBits;
|
|
||||||
#ifdef XWFEATURE_DEVID
|
|
||||||
XP_UCHAR* devIDStorage;
|
|
||||||
#endif
|
|
||||||
} AndUtil;
|
} AndUtil;
|
||||||
|
|
||||||
|
|
||||||
static VTableMgr*
|
|
||||||
and_util_getVTManager( XW_UtilCtxt* uc )
|
|
||||||
{
|
|
||||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
|
||||||
return globals->vtMgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
static XWStreamCtxt*
|
static XWStreamCtxt*
|
||||||
and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
||||||
|
@ -62,7 +61,7 @@ and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
AndUtil* util = (AndUtil*)uc;
|
AndUtil* util = (AndUtil*)uc;
|
||||||
#endif
|
#endif
|
||||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
||||||
XWStreamCtxt* stream = and_empty_stream( MPPARM(util->util.mpool)
|
XWStreamCtxt* stream = and_empty_stream( MPPARM(util->util.mpool)
|
||||||
globals );
|
globals );
|
||||||
stream_setAddress( stream, channelNo );
|
stream_setAddress( stream, channelNo );
|
||||||
|
@ -83,6 +82,14 @@ and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
||||||
__func__ ); \
|
__func__ ); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DUTIL_CBK_HEADER(nam,sig) \
|
||||||
|
AndDUtil* dutil = (AndDUtil*)duc; \
|
||||||
|
JNIEnv* env = ENVFORME( dutil->ti ); \
|
||||||
|
if ( NULL != dutil->jdutil ) { \
|
||||||
|
jmethodID mid = getMethodID( env, dutil->jdutil, nam, sig )
|
||||||
|
|
||||||
|
#define DUTIL_CBK_TAIL() UTIL_CBK_TAIL()
|
||||||
|
|
||||||
static XWBonusType and_util_getSquareBonus( XW_UtilCtxt* XP_UNUSED(uc),
|
static XWBonusType and_util_getSquareBonus( XW_UtilCtxt* XP_UNUSED(uc),
|
||||||
XP_U16 boardSize,
|
XP_U16 boardSize,
|
||||||
XP_U16 col, XP_U16 row )
|
XP_U16 col, XP_U16 row )
|
||||||
|
@ -349,26 +356,24 @@ and_util_altKeyDown( XW_UtilCtxt* uc )
|
||||||
return XP_FALSE;
|
return XP_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
XP_U32
|
XP_U32
|
||||||
and_util_getCurSeconds( XW_UtilCtxt* uc )
|
and_dutil_getCurSeconds( XW_DUtilCtxt* duc )
|
||||||
{
|
{
|
||||||
AndUtil* andutil = (AndUtil*)uc;
|
AndDUtil* anddutil = (AndDUtil*)duc;
|
||||||
XP_U32 curSeconds = getCurSeconds( ENVFORME( andutil->ti ) );
|
XP_U32 curSeconds = getCurSeconds( ENVFORME( anddutil->ti ) );
|
||||||
/* struct timeval tv; */
|
/* struct timeval tv; */
|
||||||
/* gettimeofday( &tv, NULL ); */
|
/* gettimeofday( &tv, NULL ); */
|
||||||
/* XP_LOGF( "%s: %d vs %d", __func__, (int)tv.tv_sec, (int)curSeconds ); */
|
/* XP_LOGF( "%s: %d vs %d", __func__, (int)tv.tv_sec, (int)curSeconds ); */
|
||||||
return curSeconds;
|
return curSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static DictionaryCtxt*
|
static DictionaryCtxt*
|
||||||
and_util_makeEmptyDict( XW_UtilCtxt* uc )
|
and_util_makeEmptyDict( XW_UtilCtxt* uc )
|
||||||
{
|
{
|
||||||
#ifdef STUBBED_DICT
|
#ifdef STUBBED_DICT
|
||||||
XP_ASSERT(0);
|
XP_ASSERT(0);
|
||||||
#else
|
#else
|
||||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
||||||
AndUtil* andutil = (AndUtil*)uc;
|
AndUtil* andutil = (AndUtil*)uc;
|
||||||
DictionaryCtxt* result =
|
DictionaryCtxt* result =
|
||||||
and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool )
|
and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool )
|
||||||
|
@ -378,51 +383,51 @@ and_util_makeEmptyDict( XW_UtilCtxt* uc )
|
||||||
}
|
}
|
||||||
|
|
||||||
static const XP_UCHAR*
|
static const XP_UCHAR*
|
||||||
and_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode )
|
and_dutil_getUserString( XW_DUtilCtxt* duc, XP_U16 stringCode )
|
||||||
{
|
{
|
||||||
XP_UCHAR* result = "";
|
XP_UCHAR* result = "";
|
||||||
UTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
|
DUTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
|
||||||
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
||||||
XP_ASSERT( index < VSIZE( util->userStrings ) );
|
XP_ASSERT( index < VSIZE( dutil->userStrings ) );
|
||||||
|
|
||||||
XP_ASSERT( 0 == (util->userStringsBits & (1 << index)) );
|
XP_ASSERT( 0 == (dutil->userStringsBits & (1 << index)) );
|
||||||
|
|
||||||
if ( ! util->userStrings[index] ) {
|
if ( ! dutil->userStrings[index] ) {
|
||||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid,
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
||||||
stringCode );
|
stringCode );
|
||||||
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
||||||
XP_UCHAR* buf = XP_MALLOC( util->util.mpool, len + 1 );
|
XP_UCHAR* buf = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
||||||
|
|
||||||
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
||||||
XP_MEMCPY( buf, jchars, len );
|
XP_MEMCPY( buf, jchars, len );
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
||||||
deleteLocalRef( env, jresult );
|
deleteLocalRef( env, jresult );
|
||||||
util->userStrings[index] = buf;
|
dutil->userStrings[index] = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = util->userStrings[index];
|
result = dutil->userStrings[index];
|
||||||
UTIL_CBK_TAIL();
|
DUTIL_CBK_TAIL();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const XP_UCHAR*
|
static const XP_UCHAR*
|
||||||
and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quantity )
|
and_dutil_getUserQuantityString( XW_DUtilCtxt* duc, XP_U16 stringCode, XP_U16 quantity )
|
||||||
{
|
{
|
||||||
XP_UCHAR* result = "";
|
XP_UCHAR* result = "";
|
||||||
UTIL_CBK_HEADER("getUserQuantityString", "(II)Ljava/lang/String;" );
|
DUTIL_CBK_HEADER("getUserQuantityString", "(II)Ljava/lang/String;" );
|
||||||
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
||||||
XP_ASSERT( index < VSIZE( util->userStrings ) );
|
XP_ASSERT( index < VSIZE( dutil->userStrings ) );
|
||||||
XP_UCHAR** ptrs;
|
XP_UCHAR** ptrs;
|
||||||
|
|
||||||
util->userStringsBits |= 1 << index;
|
dutil->userStringsBits |= 1 << index;
|
||||||
ptrs = (XP_UCHAR**)util->userStrings[index];
|
ptrs = (XP_UCHAR**)dutil->userStrings[index];
|
||||||
if ( !ptrs ) {
|
if ( !ptrs ) {
|
||||||
ptrs = (XP_UCHAR**)XP_CALLOC( util->util.mpool, MAX_QUANTITY_STRS * sizeof(*ptrs) );
|
ptrs = (XP_UCHAR**)XP_CALLOC( dutil->dutil.mpool, MAX_QUANTITY_STRS * sizeof(*ptrs) );
|
||||||
util->userStrings[index] = (XP_UCHAR*)ptrs;
|
dutil->userStrings[index] = (XP_UCHAR*)ptrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid,
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
||||||
stringCode, quantity );
|
stringCode, quantity );
|
||||||
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
||||||
int indx = 0;
|
int indx = 0;
|
||||||
|
@ -439,7 +444,7 @@ and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quant
|
||||||
if ( !ptrs[indx] ) {
|
if ( !ptrs[indx] ) {
|
||||||
XP_ASSERT( indx < MAX_QUANTITY_STRS );
|
XP_ASSERT( indx < MAX_QUANTITY_STRS );
|
||||||
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
||||||
XP_UCHAR* buf = XP_MALLOC( util->util.mpool, len + 1 );
|
XP_UCHAR* buf = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
||||||
XP_MEMCPY( buf, jchars, len );
|
XP_MEMCPY( buf, jchars, len );
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
ptrs[indx] = buf;
|
ptrs[indx] = buf;
|
||||||
|
@ -449,10 +454,83 @@ and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quant
|
||||||
deleteLocalRef( env, jresult );
|
deleteLocalRef( env, jresult );
|
||||||
|
|
||||||
result = ptrs[indx];
|
result = ptrs[indx];
|
||||||
UTIL_CBK_TAIL();
|
DUTIL_CBK_TAIL();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
and_dutil_storePtr( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
const void* data, XP_U16 len )
|
||||||
|
{
|
||||||
|
DUTIL_CBK_HEADER( "store", "(Ljava/lang/String;[B)V" );
|
||||||
|
|
||||||
|
jbyteArray jdata = makeByteArray( env, len, data );
|
||||||
|
jstring jkey = (*env)->NewStringUTF( env, key );
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jkey, jdata );
|
||||||
|
|
||||||
|
deleteLocalRefs( env, jdata, jkey, DELETE_NO_REF );
|
||||||
|
|
||||||
|
DUTIL_CBK_TAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
and_dutil_storeStream( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* stream )
|
||||||
|
{
|
||||||
|
const void* ptr = stream_getPtr( stream );
|
||||||
|
XP_U16 len = stream_getSize( stream );
|
||||||
|
|
||||||
|
and_dutil_storePtr( duc, key, ptr, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
static jbyteArray
|
||||||
|
loadToByteArray( XW_DUtilCtxt* duc, 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, const XP_UCHAR* key,
|
||||||
|
void* data, XP_U16* lenp )
|
||||||
|
{
|
||||||
|
AndDUtil* dutil = (AndDUtil*)duc;
|
||||||
|
JNIEnv* env = ENVFORME( dutil->ti );
|
||||||
|
jbyteArray jvalue = loadToByteArray( duc, key );
|
||||||
|
if ( jvalue != NULL ) {
|
||||||
|
jsize 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 );
|
||||||
|
}
|
||||||
|
*lenp = len;
|
||||||
|
deleteLocalRef( env, jvalue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
and_dutil_loadStream( XW_DUtilCtxt* duc, const XP_UCHAR* key, XWStreamCtxt* stream )
|
||||||
|
{
|
||||||
|
AndDUtil* dutil = (AndDUtil*)duc;
|
||||||
|
JNIEnv* env = ENVFORME( dutil->ti );
|
||||||
|
jbyteArray jvalue = loadToByteArray( duc, key );
|
||||||
|
if ( jvalue != NULL ) {
|
||||||
|
jbyte* jelems = (*env)->GetByteArrayElements( env, jvalue, NULL );
|
||||||
|
jsize len = (*env)->GetArrayLength( env, jvalue );
|
||||||
|
stream_putBytes( stream, jelems, len );
|
||||||
|
(*env)->ReleaseByteArrayElements( env, jvalue, jelems, 0 );
|
||||||
|
deleteLocalRef( env, jvalue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
and_util_notifyIllegalWords( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
and_util_notifyIllegalWords( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
||||||
XP_U16 turn, XP_Bool turnLost )
|
XP_U16 turn, XP_Bool turnLost )
|
||||||
|
@ -519,18 +597,18 @@ and_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
static XP_Bool
|
static XP_Bool
|
||||||
and_util_phoneNumbersSame( XW_UtilCtxt* uc, const XP_UCHAR* p1,
|
and_dutil_phoneNumbersSame( XW_DUtilCtxt* duc, const XP_UCHAR* p1,
|
||||||
const XP_UCHAR* p2 )
|
const XP_UCHAR* p2 )
|
||||||
{
|
{
|
||||||
XP_Bool same = 0 == strcmp( p1, p2 );
|
XP_Bool same = 0 == strcmp( p1, p2 );
|
||||||
if ( !same ) {
|
if ( !same ) {
|
||||||
UTIL_CBK_HEADER( "phoneNumbersSame",
|
DUTIL_CBK_HEADER( "phoneNumbersSame",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z" );
|
"(Ljava/lang/String;Ljava/lang/String;)Z" );
|
||||||
jstring js1 = (*env)->NewStringUTF( env, p1 );
|
jstring js1 = (*env)->NewStringUTF( env, p1 );
|
||||||
jstring js2 = (*env)->NewStringUTF( env, p2 );
|
jstring js2 = (*env)->NewStringUTF( env, p2 );
|
||||||
same = (*env)->CallBooleanMethod( env, util->jutil, mid, js1, js2 );
|
same = (*env)->CallBooleanMethod( env, dutil->jdutil, mid, js1, js2 );
|
||||||
deleteLocalRefs( env, js1, js2, DELETE_NO_REF );
|
deleteLocalRefs( env, js1, js2, DELETE_NO_REF );
|
||||||
UTIL_CBK_TAIL();
|
DUTIL_CBK_TAIL();
|
||||||
}
|
}
|
||||||
return same;
|
return same;
|
||||||
}
|
}
|
||||||
|
@ -587,50 +665,50 @@ and_util_setIsServer(XW_UtilCtxt* uc, XP_Bool isServer )
|
||||||
|
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
static const XP_UCHAR*
|
static const XP_UCHAR*
|
||||||
and_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
|
and_dutil_getDevID( XW_DUtilCtxt* duc, DevIDType* typ )
|
||||||
{
|
{
|
||||||
const XP_UCHAR* result = NULL;
|
const XP_UCHAR* result = NULL;
|
||||||
*typ = ID_TYPE_NONE;
|
*typ = ID_TYPE_NONE;
|
||||||
UTIL_CBK_HEADER( "getDevID", "([B)Ljava/lang/String;" );
|
DUTIL_CBK_HEADER( "getDevID", "([B)Ljava/lang/String;" );
|
||||||
jbyteArray jbarr = makeByteArray( env, 1, NULL );
|
jbyteArray jbarr = makeByteArray( env, 1, NULL );
|
||||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid, jbarr );
|
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid, jbarr );
|
||||||
if ( NULL != jresult ) {
|
if ( NULL != jresult ) {
|
||||||
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
||||||
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
||||||
if ( NULL != util->devIDStorage
|
if ( NULL != dutil->devIDStorage
|
||||||
&& 0 == XP_MEMCMP( util->devIDStorage, jchars, len ) ) {
|
&& 0 == XP_MEMCMP( dutil->devIDStorage, jchars, len ) ) {
|
||||||
XP_LOGF( "%s: already have matching devID", __func__ );
|
XP_LOGF( "%s: already have matching devID", __func__ );
|
||||||
} else {
|
} else {
|
||||||
XP_LOGF( "%s: allocating storage for devID", __func__ );
|
XP_LOGF( "%s: allocating storage for devID", __func__ );
|
||||||
XP_FREEP( util->util.mpool, &util->devIDStorage );
|
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
||||||
util->devIDStorage = XP_MALLOC( util->util.mpool, len + 1 );
|
dutil->devIDStorage = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
||||||
XP_MEMCPY( util->devIDStorage, jchars, len );
|
XP_MEMCPY( dutil->devIDStorage, jchars, len );
|
||||||
util->devIDStorage[len] = '\0';
|
dutil->devIDStorage[len] = '\0';
|
||||||
}
|
}
|
||||||
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
||||||
result = (const XP_UCHAR*)util->devIDStorage;
|
result = (const XP_UCHAR*)dutil->devIDStorage;
|
||||||
|
|
||||||
jbyte* elems = (*env)->GetByteArrayElements( env, jbarr, NULL );
|
jbyte* elems = (*env)->GetByteArrayElements( env, jbarr, NULL );
|
||||||
*typ = (DevIDType)elems[0];
|
*typ = (DevIDType)elems[0];
|
||||||
(*env)->ReleaseByteArrayElements( env, jbarr, elems, 0 );
|
(*env)->ReleaseByteArrayElements( env, jbarr, elems, 0 );
|
||||||
}
|
}
|
||||||
deleteLocalRef( env, jbarr );
|
deleteLocalRef( env, jbarr );
|
||||||
UTIL_CBK_TAIL();
|
DUTIL_CBK_TAIL();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
and_util_deviceRegistered( XW_UtilCtxt* uc, DevIDType typ,
|
and_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
|
||||||
const XP_UCHAR* idRelay )
|
const XP_UCHAR* idRelay )
|
||||||
{
|
{
|
||||||
UTIL_CBK_HEADER( "deviceRegistered",
|
DUTIL_CBK_HEADER( "deviceRegistered",
|
||||||
"(L" PKG_PATH("jni/UtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
"(L" PKG_PATH("jni/UtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
||||||
jstring jstr = (*env)->NewStringUTF( env, idRelay );
|
jstring jstr = (*env)->NewStringUTF( env, idRelay );
|
||||||
jobject jtyp = intToJEnum( env, typ,
|
jobject jtyp = intToJEnum( env, typ,
|
||||||
PKG_PATH("jni/UtilCtxt$DevIDType") );
|
PKG_PATH("jni/UtilCtxt$DevIDType") );
|
||||||
(*env)->CallVoidMethod( env, util->jutil, mid, jtyp, jstr );
|
(*env)->CallVoidMethod( env, dutil->jdutil, mid, jtyp, jstr );
|
||||||
deleteLocalRefs( env, jstr, jtyp, DELETE_NO_REF );
|
deleteLocalRefs( env, jstr, jtyp, DELETE_NO_REF );
|
||||||
UTIL_CBK_TAIL();
|
DUTIL_CBK_TAIL();
|
||||||
}
|
}
|
||||||
#endif /* XWFEATURE_DEVID */
|
#endif /* XWFEATURE_DEVID */
|
||||||
|
|
||||||
|
@ -664,16 +742,23 @@ and_util_engineStopping( XW_UtilCtxt* uc )
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static XW_DUtilCtxt*
|
||||||
|
and_util_getDevUtilCtxt( XW_UtilCtxt* uc )
|
||||||
|
{
|
||||||
|
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
||||||
|
XP_ASSERT( !!globals->dutil );
|
||||||
|
return globals->dutil;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef COMMS_CHECKSUM
|
#ifdef COMMS_CHECKSUM
|
||||||
static XP_UCHAR*
|
static XP_UCHAR*
|
||||||
and_util_md5sum( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len )
|
and_dutil_md5sum( XW_DUtilCtxt* duc, const XP_U8* ptr, XP_U16 len )
|
||||||
{
|
{
|
||||||
AndUtil* util = (AndUtil*)uc;
|
AndDUtil* dutil = (AndDUtil*)duc;
|
||||||
JNIEnv* env = ENVFORME( util->ti );
|
JNIEnv* env = ENVFORME( dutil->ti );
|
||||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
struct JNIUtilCtxt* jniutil = dutil->jniutil;
|
||||||
struct JNIUtilCtxt* jniutil = globals->jniutil;
|
|
||||||
jstring jsum = and_util_getMD5SumForBytes( jniutil, ptr, len );
|
jstring jsum = and_util_getMD5SumForBytes( jniutil, ptr, len );
|
||||||
XP_UCHAR* result = getStringCopy( MPPARM(uc->mpool) env, jsum );
|
XP_UCHAR* result = getStringCopy( MPPARM(duc->mpool) env, jsum );
|
||||||
deleteLocalRef( env, jsum );
|
deleteLocalRef( env, jsum );
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -682,7 +767,7 @@ and_util_md5sum( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len )
|
||||||
|
|
||||||
XW_UtilCtxt*
|
XW_UtilCtxt*
|
||||||
makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
AndGlobals* closure )
|
AndGameGlobals* closure )
|
||||||
{
|
{
|
||||||
AndUtil* util = (AndUtil*)XP_CALLOC( mpool, sizeof(*util) );
|
AndUtil* util = (AndUtil*)XP_CALLOC( mpool, sizeof(*util) );
|
||||||
UtilVtable* vtable = (UtilVtable*)XP_CALLOC( mpool, sizeof(*vtable) );
|
UtilVtable* vtable = (UtilVtable*)XP_CALLOC( mpool, sizeof(*vtable) );
|
||||||
|
@ -697,7 +782,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
util->util.gameInfo = gi;
|
util->util.gameInfo = gi;
|
||||||
|
|
||||||
#define SET_PROC(nam) vtable->m_util_##nam = and_util_##nam
|
#define SET_PROC(nam) vtable->m_util_##nam = and_util_##nam
|
||||||
SET_PROC(getVTManager);
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
SET_PROC(makeStreamFromAddr);
|
SET_PROC(makeStreamFromAddr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -725,10 +810,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
SET_PROC(clearTimer);
|
SET_PROC(clearTimer);
|
||||||
SET_PROC(requestTime);
|
SET_PROC(requestTime);
|
||||||
SET_PROC(altKeyDown);
|
SET_PROC(altKeyDown);
|
||||||
SET_PROC(getCurSeconds);
|
|
||||||
SET_PROC(makeEmptyDict);
|
SET_PROC(makeEmptyDict);
|
||||||
SET_PROC(getUserString);
|
|
||||||
SET_PROC(getUserQuantityString);
|
|
||||||
SET_PROC(notifyIllegalWords);
|
SET_PROC(notifyIllegalWords);
|
||||||
#ifdef XWFEATURE_CHAT
|
#ifdef XWFEATURE_CHAT
|
||||||
SET_PROC(showChat);
|
SET_PROC(showChat);
|
||||||
|
@ -740,10 +822,6 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
SET_PROC(playerScoreHeld);
|
SET_PROC(playerScoreHeld);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
|
||||||
SET_PROC(phoneNumbersSame);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef XWFEATURE_BOARDWORDS
|
#ifdef XWFEATURE_BOARDWORDS
|
||||||
SET_PROC(cellSquareHeld);
|
SET_PROC(cellSquareHeld);
|
||||||
#endif
|
#endif
|
||||||
|
@ -752,10 +830,6 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
SET_PROC(informMissing);
|
SET_PROC(informMissing);
|
||||||
SET_PROC(addrChange);
|
SET_PROC(addrChange);
|
||||||
SET_PROC(setIsServer);
|
SET_PROC(setIsServer);
|
||||||
# ifdef XWFEATURE_DEVID
|
|
||||||
SET_PROC(getDevID);
|
|
||||||
SET_PROC(deviceRegistered);
|
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_SEARCHLIMIT
|
#ifdef XWFEATURE_SEARCHLIMIT
|
||||||
SET_PROC(getTraySearchLimits);
|
SET_PROC(getTraySearchLimits);
|
||||||
|
@ -764,9 +838,8 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||||
SET_PROC(engineStarting);
|
SET_PROC(engineStarting);
|
||||||
SET_PROC(engineStopping);
|
SET_PROC(engineStopping);
|
||||||
#endif
|
#endif
|
||||||
#ifdef COMMS_CHECKSUM
|
|
||||||
SET_PROC(md5sum);
|
SET_PROC(getDevUtilCtxt);
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef SET_PROC
|
#undef SET_PROC
|
||||||
return (XW_UtilCtxt*)util;
|
return (XW_UtilCtxt*)util;
|
||||||
|
@ -778,31 +851,81 @@ destroyUtil( XW_UtilCtxt** utilc )
|
||||||
AndUtil* util = (AndUtil*)*utilc;
|
AndUtil* util = (AndUtil*)*utilc;
|
||||||
JNIEnv* env = ENVFORME( util->ti );
|
JNIEnv* env = ENVFORME( util->ti );
|
||||||
|
|
||||||
for ( int ii = 0; ii < VSIZE(util->userStrings); ++ii ) {
|
if ( NULL != util->jutil ) {
|
||||||
XP_UCHAR* ptr = util->userStrings[ii];
|
(*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 EnvThreadInfo* ti, jobject jdutil, VTableMgr* vtMgr,
|
||||||
|
JNIUtilCtxt* jniutil, void* closure )
|
||||||
|
{
|
||||||
|
AndDUtil* dutil = (AndDUtil*)XP_CALLOC( mpool, sizeof(*dutil) );
|
||||||
|
dutil->ti = ti;
|
||||||
|
dutil->jniutil = jniutil;
|
||||||
|
dutil->dutil.closure = closure;
|
||||||
|
dutil->dutil.vtMgr = vtMgr;
|
||||||
|
|
||||||
|
if ( NULL != jdutil ) {
|
||||||
|
JNIEnv* env = ENVFORME( ti );
|
||||||
|
dutil->jdutil = (*env)->NewGlobalRef( env, jdutil );
|
||||||
|
}
|
||||||
|
|
||||||
|
MPASSIGN( dutil->dutil.mpool, mpool );
|
||||||
|
|
||||||
|
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(storeStream);
|
||||||
|
SET_DPROC(loadStream);
|
||||||
|
SET_DPROC(storePtr);
|
||||||
|
SET_DPROC(loadPtr);
|
||||||
|
# ifdef XWFEATURE_DEVID
|
||||||
|
SET_DPROC(getDevID);
|
||||||
|
SET_DPROC(deviceRegistered);
|
||||||
|
# endif
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
SET_DPROC(phoneNumbersSame);
|
||||||
|
#endif
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
SET_DPROC(md5sum);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return &dutil->dutil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
destroyDUtil( XW_DUtilCtxt** dutilp )
|
||||||
|
{
|
||||||
|
AndDUtil* dutil = (AndDUtil*)*dutilp;
|
||||||
|
JNIEnv* env = ENVFORME( dutil->ti );
|
||||||
|
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 ( NULL != ptr ) {
|
||||||
if ( 0 == (util->userStringsBits & (1 << ii)) ) {
|
if ( 0 == (dutil->userStringsBits & (1 << ii)) ) {
|
||||||
XP_FREE( util->util.mpool, ptr );
|
XP_FREE( dutil->dutil.mpool, ptr );
|
||||||
} else {
|
} else {
|
||||||
XP_UCHAR** ptrs = (XP_UCHAR**)ptr;
|
XP_UCHAR** ptrs = (XP_UCHAR**)ptr;
|
||||||
for ( int jj = 0; jj < MAX_QUANTITY_STRS; ++jj ) {
|
for ( int jj = 0; jj < MAX_QUANTITY_STRS; ++jj ) {
|
||||||
ptr = ptrs[jj];
|
ptr = ptrs[jj];
|
||||||
if ( !!ptr ) {
|
if ( !!ptr ) {
|
||||||
XP_FREE( util->util.mpool, ptr );
|
XP_FREE( dutil->dutil.mpool, ptr );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XP_FREE( util->util.mpool, ptrs );
|
XP_FREE( dutil->dutil.mpool, ptrs );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL != util->jutil ) {
|
|
||||||
(*env)->DeleteGlobalRef( env, util->jutil );
|
|
||||||
}
|
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
XP_FREEP( util->util.mpool, &util->devIDStorage );
|
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
||||||
#endif
|
#endif
|
||||||
XP_FREE( util->util.mpool, util->util.vtable );
|
|
||||||
XP_FREE( util->util.mpool, util );
|
|
||||||
*utilc = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,17 @@
|
||||||
|
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "dutil.h"
|
||||||
#include "andglobals.h"
|
#include "andglobals.h"
|
||||||
|
#include "jniutlswrapper.h"
|
||||||
|
|
||||||
|
XW_DUtilCtxt* makeDUtil( MPFORMAL EnvThreadInfo* ti, jobject j_dutil,
|
||||||
|
VTableMgr* vtMgr, JNIUtilCtxt* jniutil,
|
||||||
|
void* closure );
|
||||||
|
void destroyDUtil( XW_DUtilCtxt** dutilp );
|
||||||
|
|
||||||
XW_UtilCtxt* makeUtil( MPFORMAL EnvThreadInfo* ti, jobject j_util,
|
XW_UtilCtxt* makeUtil( MPFORMAL EnvThreadInfo* ti, jobject j_util,
|
||||||
CurGameInfo* gi, AndGlobals* globals );
|
CurGameInfo* gi, AndGameGlobals* globals );
|
||||||
void destroyUtil( XW_UtilCtxt** util );
|
void destroyUtil( XW_UtilCtxt** util );
|
||||||
|
|
||||||
bool utilTimerFired( XW_UtilCtxt* util, XWTimerReason why, int handle );
|
bool utilTimerFired( XW_UtilCtxt* util, XWTimerReason why, int handle );
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* -*- compile-command: "find-and-gradle.sh installXw4Debug"; -*- */
|
|
||||||
|
/* -*- compile-command: "find-and-gradle.sh inXw4Deb"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright © 2009 - 2014 by Eric House (xwords@eehouse.org). All rights
|
* Copyright © 2009 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#include "dictiter.h"
|
#include "dictiter.h"
|
||||||
#include "dictmgr.h"
|
#include "dictmgr.h"
|
||||||
#include "nli.h"
|
#include "nli.h"
|
||||||
|
#include "smsproto.h"
|
||||||
|
|
||||||
#include "utilwrapper.h"
|
#include "utilwrapper.h"
|
||||||
#include "drawwrapper.h"
|
#include "drawwrapper.h"
|
||||||
|
@ -60,9 +62,33 @@ struct _EnvThreadInfo {
|
||||||
typedef struct _JNIGlobalState {
|
typedef struct _JNIGlobalState {
|
||||||
EnvThreadInfo ti;
|
EnvThreadInfo ti;
|
||||||
DictMgrCtxt* dictMgr;
|
DictMgrCtxt* dictMgr;
|
||||||
|
SMSProto* smsProto;
|
||||||
|
VTableMgr* vtMgr;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
|
JNIUtilCtxt* jniutil;
|
||||||
|
XP_Bool mpoolInUse;
|
||||||
MPSLOT
|
MPSLOT
|
||||||
} JNIGlobalState;
|
} JNIGlobalState;
|
||||||
|
|
||||||
|
#ifdef MEM_DEBUG
|
||||||
|
static MemPoolCtx*
|
||||||
|
getMPool( JNIGlobalState* globalState )
|
||||||
|
{
|
||||||
|
XP_ASSERT( !globalState->mpoolInUse );
|
||||||
|
globalState->mpoolInUse = XP_TRUE;
|
||||||
|
return globalState->mpool;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
releaseMPool( JNIGlobalState* globalState )
|
||||||
|
{
|
||||||
|
XP_ASSERT( globalState->mpoolInUse );
|
||||||
|
globalState->mpoolInUse = XP_FALSE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define releaseMPool(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define LOG_MAPPING
|
#define LOG_MAPPING
|
||||||
// #define LOG_MAPPING_ALL
|
// #define LOG_MAPPING_ALL
|
||||||
|
|
||||||
|
@ -229,6 +255,7 @@ static void
|
||||||
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
|
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
|
||||||
{
|
{
|
||||||
if ( jtiles != NULL ) {
|
if ( jtiles != NULL ) {
|
||||||
|
XP_ASSERT( !!jtiles );
|
||||||
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
|
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
|
||||||
int tmp[MAX_TRAY_TILES];
|
int tmp[MAX_TRAY_TILES];
|
||||||
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
|
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
|
||||||
|
@ -255,33 +282,43 @@ getState( JNIEnv* env, GamePtrType gamePtr )
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals
|
Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals
|
||||||
( JNIEnv* env, jclass C )
|
( JNIEnv* env, jclass C, jobject jdutil, jobject jniu )
|
||||||
{
|
{
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = mpool_make( NULL );
|
MemPoolCtx* mpool = mpool_make( NULL );
|
||||||
#endif
|
#endif
|
||||||
JNIGlobalState* state = (JNIGlobalState*)XP_CALLOC( mpool, sizeof(*state) );
|
JNIGlobalState* globalState = (JNIGlobalState*)XP_CALLOC( mpool,
|
||||||
map_init( MPPARM(mpool) &state->ti, env );
|
sizeof(*globalState) );
|
||||||
state->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
|
map_init( MPPARM(mpool) &globalState->ti, env );
|
||||||
MPASSIGN( state->mpool, mpool );
|
globalState->jniutil = makeJNIUtil( MPPARM(mpool) env, &globalState->ti, jniu );
|
||||||
// LOG_RETURNF( "%p", state );
|
globalState->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||||
return (jint)state;
|
globalState->dutil = makeDUtil( MPPARM(mpool) &globalState->ti, jdutil,
|
||||||
|
globalState->vtMgr, globalState->jniutil, NULL );
|
||||||
|
globalState->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
|
||||||
|
globalState->smsProto = smsproto_init( MPPARM( mpool ) globalState->dutil );
|
||||||
|
MPASSIGN( globalState->mpool, mpool );
|
||||||
|
// LOG_RETURNF( "%p", globalState );
|
||||||
|
return (jint)globalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals
|
Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals
|
||||||
( JNIEnv* env, jclass C, jint ptr )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr )
|
||||||
{
|
{
|
||||||
// LOG_FUNC();
|
// LOG_FUNC();
|
||||||
if ( 0 != ptr ) {
|
if ( 0 != jniGlobalPtr ) {
|
||||||
JNIGlobalState* state = (JNIGlobalState*)ptr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
XP_ASSERT( ENVFORME(&state->ti) == env );
|
|
||||||
dmgr_destroy( state->dictMgr );
|
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = state->mpool;
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
#endif
|
#endif
|
||||||
map_destroy( &state->ti );
|
XP_ASSERT( ENVFORME(&globalState->ti) == env );
|
||||||
XP_FREE( mpool, state );
|
smsproto_free( globalState->smsProto );
|
||||||
|
vtmgr_destroy( MPPARM(mpool) globalState->vtMgr );
|
||||||
|
dmgr_destroy( globalState->dictMgr );
|
||||||
|
destroyDUtil( &globalState->dutil );
|
||||||
|
destroyJNIUtil( env, &globalState->jniutil );
|
||||||
|
map_destroy( &globalState->ti );
|
||||||
|
XP_FREE( mpool, globalState );
|
||||||
mpool_destroy( mpool );
|
mpool_destroy( mpool );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,6 +548,7 @@ loadCommonPrefs( JNIEnv* env, CommonPrefs* cp, jobject j_cp )
|
||||||
static XWStreamCtxt*
|
static XWStreamCtxt*
|
||||||
streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream )
|
streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream )
|
||||||
{
|
{
|
||||||
|
XP_ASSERT( !!jstream );
|
||||||
int len = (*env)->GetArrayLength( env, jstream );
|
int len = (*env)->GetArrayLength( env, jstream );
|
||||||
XWStreamCtxt* stream = mem_stream_make_sized( MPPARM(mpool) vtMgr,
|
XWStreamCtxt* stream = mem_stream_make_sized( MPPARM(mpool) vtMgr,
|
||||||
len, NULL, 0, NULL );
|
len, NULL, 0, NULL );
|
||||||
|
@ -525,15 +563,15 @@ streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream )
|
||||||
****************************************************/
|
****************************************************/
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream
|
Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream
|
||||||
(JNIEnv* env, jclass C, jobject jgi )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jgi )
|
||||||
{
|
{
|
||||||
jbyteArray result;
|
jbyteArray result;
|
||||||
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = mpool_make( NULL );
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
#endif
|
#endif
|
||||||
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
|
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
|
||||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globalState->vtMgr,
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr,
|
|
||||||
NULL, 0, NULL );
|
NULL, 0, NULL );
|
||||||
|
|
||||||
game_saveToStream( NULL, gi, stream, 0 );
|
game_saveToStream( NULL, gi, stream, 0 );
|
||||||
|
@ -541,24 +579,20 @@ Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream
|
||||||
|
|
||||||
result = streamToBArray( env, stream );
|
result = streamToBArray( env, stream );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
releaseMPool( globalState );
|
||||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
mpool_destroy( mpool );
|
|
||||||
#endif
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_gi_1from_1stream
|
Java_org_eehouse_android_xw4_jni_XwJNI_gi_1from_1stream
|
||||||
( JNIEnv* env, jclass C, jobject jgi, jbyteArray jstream )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jgi, jbyteArray jstream )
|
||||||
{
|
{
|
||||||
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = mpool_make( NULL );
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
#endif
|
#endif
|
||||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
|
||||||
|
globalState->vtMgr, jstream );
|
||||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, vtMgr, jstream );
|
|
||||||
|
|
||||||
CurGameInfo gi;
|
CurGameInfo gi;
|
||||||
XP_MEMSET( &gi, 0, sizeof(gi) );
|
XP_MEMSET( &gi, 0, sizeof(gi) );
|
||||||
|
@ -572,50 +606,45 @@ Java_org_eehouse_android_xw4_jni_XwJNI_gi_1from_1stream
|
||||||
gi_disposePlayerInfo( MPPARM(mpool) &gi );
|
gi_disposePlayerInfo( MPPARM(mpool) &gi );
|
||||||
|
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
releaseMPool( globalState );
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
mpool_destroy( mpool );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1to_1stream
|
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1to_1stream
|
||||||
( JNIEnv* env, jclass C, jobject njli )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject njli )
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
jbyteArray result;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = mpool_make( NULL );
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
jbyteArray result;
|
||||||
NetLaunchInfo nli = {0};
|
NetLaunchInfo nli = {0};
|
||||||
loadNLI( env, &nli, njli );
|
loadNLI( env, &nli, njli );
|
||||||
/* CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi ); */
|
/* CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi ); */
|
||||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globalState->vtMgr,
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr,
|
|
||||||
NULL, 0, NULL );
|
NULL, 0, NULL );
|
||||||
|
|
||||||
nli_saveToStream( &nli, stream );
|
nli_saveToStream( &nli, stream );
|
||||||
|
|
||||||
result = streamToBArray( env, stream );
|
result = streamToBArray( env, stream );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
releaseMPool( globalState );
|
||||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
mpool_destroy( mpool );
|
|
||||||
#endif
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1from_1stream
|
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1from_1stream
|
||||||
( JNIEnv* env, jclass C, jobject jnli, jbyteArray jstream )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jnli, jbyteArray jstream )
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = mpool_make( NULL );
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
#endif
|
#endif
|
||||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
|
||||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, vtMgr, jstream );
|
globalState->vtMgr, jstream );
|
||||||
|
|
||||||
NetLaunchInfo nli = {0};
|
NetLaunchInfo nli = {0};
|
||||||
if ( nli_makeFromStream( &nli, stream ) ) {
|
if ( nli_makeFromStream( &nli, stream ) ) {
|
||||||
|
@ -625,10 +654,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_nli_1from_1stream
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
releaseMPool( globalState );
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
mpool_destroy( mpool );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
|
@ -684,14 +710,18 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1unref
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
||||||
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes,
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes,
|
||||||
jstring jname, jstring jpath, jobject jniu, jboolean check, jobject jinfo )
|
jstring jname, jstring jpath, jboolean check, jobject jinfo )
|
||||||
{
|
{
|
||||||
jboolean result = false;
|
jboolean result = false;
|
||||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
map_thread( &state->ti, env );
|
map_thread( &globalState->ti, env );
|
||||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) env, &state->ti, jniu );
|
|
||||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
#ifdef MEM_DEBUG
|
||||||
jniutil, jname, jDictBytes, jpath,
|
MemPoolCtx* mpool = getMPool( globalState );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, globalState->dictMgr,
|
||||||
|
globalState->jniutil, jname, jDictBytes, jpath,
|
||||||
NULL, check );
|
NULL, check );
|
||||||
if ( NULL != dict ) {
|
if ( NULL != dict ) {
|
||||||
if ( NULL != jinfo ) {
|
if ( NULL != jinfo ) {
|
||||||
|
@ -704,8 +734,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
||||||
dict_unref( dict );
|
dict_unref( dict );
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
destroyJNIUtil( env, &jniutil );
|
|
||||||
|
|
||||||
|
releaseMPool( globalState );
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,10 +769,88 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getTileValue
|
||||||
return dict_getTileValue( (DictionaryCtxt*)dictPtr, tile );
|
return dict_getTileValue( (DictionaryCtxt*)dictPtr, tile );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jobjectArray
|
||||||
|
msgArrayToByteArrays( JNIEnv* env, const SMSMsgArray* arr )
|
||||||
|
{
|
||||||
|
jclass clas = (*env)->FindClass( env, "[B" );
|
||||||
|
jobjectArray result = (*env)->NewObjectArray( env, arr->nMsgs, clas, NULL );
|
||||||
|
for ( int ii = 0; ii < arr->nMsgs; ++ii ) {
|
||||||
|
SMSMsg* msg = &arr->msgs[ii];
|
||||||
|
jbyteArray arr = makeByteArray( env, msg->len, (const jbyte*)msg->data );
|
||||||
|
(*env)->SetObjectArrayElement( env, result, ii, arr );
|
||||||
|
deleteLocalRef( env, arr );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepOutbound
|
||||||
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jData,
|
||||||
|
jstring jToPhone, jint jNow, jboolean jForce, jintArray jWaitSecsArr )
|
||||||
|
{
|
||||||
|
jobjectArray result = NULL;
|
||||||
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
|
map_thread( &globalState->ti, env );
|
||||||
|
|
||||||
|
jbyte* data = NULL;
|
||||||
|
int len = 0;
|
||||||
|
if ( NULL != jData ) {
|
||||||
|
len = (*env)->GetArrayLength( env, jData );
|
||||||
|
data = (*env)->GetByteArrayElements( env, jData, NULL );
|
||||||
|
}
|
||||||
|
const char* toPhone = (*env)->GetStringUTFChars( env, jToPhone, NULL );
|
||||||
|
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
SMSMsgArray* arr = smsproto_prepOutbound( globalState->smsProto, (const XP_U8*)data,
|
||||||
|
len, toPhone, jForce, &waitSecs );
|
||||||
|
if ( !!arr ) {
|
||||||
|
result = msgArrayToByteArrays( env, arr );
|
||||||
|
smsproto_freeMsgArray( globalState->smsProto, arr );
|
||||||
|
}
|
||||||
|
|
||||||
|
setIntInArray( env, jWaitSecsArr, 0, waitSecs );
|
||||||
|
|
||||||
|
(*env)->ReleaseStringUTFChars( env, jToPhone, toPhone );
|
||||||
|
if ( NULL != jData ) {
|
||||||
|
(*env)->ReleaseByteArrayElements( env, jData, data, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepInbound
|
||||||
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jData,
|
||||||
|
jstring jFromPhone )
|
||||||
|
{
|
||||||
|
jobjectArray result = NULL;
|
||||||
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
|
map_thread( &globalState->ti, env );
|
||||||
|
|
||||||
|
if ( !!jData ) {
|
||||||
|
int len = (*env)->GetArrayLength( env, jData );
|
||||||
|
jbyte* data = (*env)->GetByteArrayElements( env, jData, NULL );
|
||||||
|
const char* fromPhone = (*env)->GetStringUTFChars( env, jFromPhone, NULL );
|
||||||
|
|
||||||
|
SMSMsgArray* arr = smsproto_prepInbound( globalState->smsProto, fromPhone,
|
||||||
|
(XP_U8*)data, len );
|
||||||
|
if ( !!arr ) {
|
||||||
|
result = msgArrayToByteArrays( env, arr );
|
||||||
|
smsproto_freeMsgArray( globalState->smsProto, arr );
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->ReleaseStringUTFChars( env, jFromPhone, fromPhone );
|
||||||
|
(*env)->ReleaseByteArrayElements( env, jData, data, 0 );
|
||||||
|
} else {
|
||||||
|
XP_LOGF( "%s() => null (null input)", __func__ );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct _JNIState {
|
struct _JNIState {
|
||||||
XWGame game;
|
XWGame game;
|
||||||
JNIGlobalState* globalJNI;
|
JNIGlobalState* globalJNI;
|
||||||
AndGlobals globals;
|
AndGameGlobals globals;
|
||||||
// pthread_mutex_t msgMutex;
|
// pthread_mutex_t msgMutex;
|
||||||
XP_U16 curSaveCount;
|
XP_U16 curSaveCount;
|
||||||
XP_U16 lastSavedSize;
|
XP_U16 lastSavedSize;
|
||||||
|
@ -761,27 +869,26 @@ struct _JNIState {
|
||||||
|
|
||||||
#define XWJNI_START_GLOBALS() \
|
#define XWJNI_START_GLOBALS() \
|
||||||
XWJNI_START() \
|
XWJNI_START() \
|
||||||
AndGlobals* globals = &state->globals; \
|
AndGameGlobals* globals = &state->globals; \
|
||||||
|
|
||||||
#define XWJNI_END() \
|
#define XWJNI_END() \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_initJNI
|
Java_org_eehouse_android_xw4_jni_XwJNI_initJNI
|
||||||
( JNIEnv* env, jclass C, int jniGlobalPtr, jint seed, jstring jtag )
|
( JNIEnv* env, jclass C, int jniGlobalPtr, jint seed )
|
||||||
{
|
{
|
||||||
/* Why am I doing this twice? */
|
/* Why am I doing this twice? */
|
||||||
/* struct timeval tv; */
|
/* struct timeval tv; */
|
||||||
/* gettimeofday( &tv, NULL ); */
|
/* gettimeofday( &tv, NULL ); */
|
||||||
/* srandom( tv.tv_sec ); */
|
/* srandom( tv.tv_sec ); */
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
const char* tag = (*env)->GetStringUTFChars( env, jtag, NULL );
|
MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool;
|
||||||
MemPoolCtx* mpool = mpool_make( tag );
|
|
||||||
(*env)->ReleaseStringUTFChars( env, jtag, tag );
|
|
||||||
#endif
|
#endif
|
||||||
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
||||||
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
||||||
AndGlobals* globals = &state->globals;
|
AndGameGlobals* globals = &state->globals;
|
||||||
|
globals->dutil = state->globalJNI->dutil;
|
||||||
globals->state = (JNIState*)state;
|
globals->state = (JNIState*)state;
|
||||||
MPASSIGN( state->mpool, mpool );
|
MPASSIGN( state->mpool, mpool );
|
||||||
globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool));
|
globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool));
|
||||||
|
@ -807,8 +914,8 @@ JNIEXPORT void JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
||||||
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi,
|
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi,
|
||||||
jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths,
|
jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths,
|
||||||
jstring j_lang, jobject j_util, jobject jniu, jobject j_draw,
|
jstring j_lang, jobject j_util, jobject j_draw, jobject j_cp,
|
||||||
jobject j_cp, jobject j_procs )
|
jobject j_procs )
|
||||||
{
|
{
|
||||||
XWJNI_START_GLOBALS();
|
XWJNI_START_GLOBALS();
|
||||||
EnvThreadInfo* ti = &state->globalJNI->ti;
|
EnvThreadInfo* ti = &state->globalJNI->ti;
|
||||||
|
@ -816,7 +923,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
||||||
globals->gi = gi;
|
globals->gi = gi;
|
||||||
globals->util = makeUtil( MPPARM(mpool) ti, j_util, gi,
|
globals->util = makeUtil( MPPARM(mpool) ti, j_util, gi,
|
||||||
globals );
|
globals );
|
||||||
globals->jniutil = makeJNIUtil( MPPARM(mpool) env, ti, jniu );
|
globals->jniutil = state->globalJNI->jniutil;
|
||||||
DrawCtx* dctx = NULL;
|
DrawCtx* dctx = NULL;
|
||||||
if ( !!j_draw ) {
|
if ( !!j_draw ) {
|
||||||
dctx = makeDraw( MPPARM(mpool) ti, j_draw );
|
dctx = makeDraw( MPPARM(mpool) ti, j_draw );
|
||||||
|
@ -858,7 +965,7 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = state->mpool;
|
MemPoolCtx* mpool = state->mpool;
|
||||||
#endif
|
#endif
|
||||||
AndGlobals* globals = &state->globals;
|
AndGameGlobals* globals = &state->globals;
|
||||||
|
|
||||||
destroyGI( MPPARM(mpool) &globals->gi );
|
destroyGI( MPPARM(mpool) &globals->gi );
|
||||||
|
|
||||||
|
@ -867,22 +974,19 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
|
||||||
destroyDraw( &globals->dctx );
|
destroyDraw( &globals->dctx );
|
||||||
destroyXportProcs( &globals->xportProcs );
|
destroyXportProcs( &globals->xportProcs );
|
||||||
destroyUtil( &globals->util );
|
destroyUtil( &globals->util );
|
||||||
destroyJNIUtil( env, &globals->jniutil );
|
|
||||||
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
||||||
|
|
||||||
MAP_REMOVE( &state->globalJNI->ti, env );
|
MAP_REMOVE( &state->globalJNI->ti, env );
|
||||||
/* pthread_mutex_destroy( &state->msgMutex ); */
|
/* pthread_mutex_destroy( &state->msgMutex ); */
|
||||||
|
|
||||||
XP_FREE( mpool, state );
|
XP_FREE( mpool, state );
|
||||||
mpool_destroy( mpool );
|
|
||||||
} /* game_dispose */
|
} /* game_dispose */
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
||||||
( JNIEnv* env, jclass C, GamePtrType gamePtr, jbyteArray jstream, jobject /*out*/jgi,
|
( JNIEnv* env, jclass C, GamePtrType gamePtr, jbyteArray jstream, jobject /*out*/jgi,
|
||||||
jobjectArray jdictNames, jobjectArray jdicts, jobjectArray jpaths,
|
jobjectArray jdictNames, jobjectArray jdicts, jobjectArray jpaths,
|
||||||
jstring jlang, jobject jutil, jobject jniu, jobject jdraw, jobject jcp,
|
jstring jlang, jobject jutil, jobject jdraw, jobject jcp, jobject jprocs )
|
||||||
jobject jprocs )
|
|
||||||
{
|
{
|
||||||
jboolean result;
|
jboolean result;
|
||||||
DictionaryCtxt* dict;
|
DictionaryCtxt* dict;
|
||||||
|
@ -892,7 +996,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
||||||
|
|
||||||
globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) );
|
globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) );
|
||||||
globals->util = makeUtil( MPPARM(mpool) ti, jutil, globals->gi, globals);
|
globals->util = makeUtil( MPPARM(mpool) ti, jutil, globals->gi, globals);
|
||||||
globals->jniutil = makeJNIUtil( MPPARM(mpool) env, ti, jniu );
|
globals->jniutil = state->globalJNI->jniutil;
|
||||||
makeDicts( MPPARM(state->globalJNI->mpool) env, state->globalJNI->dictMgr,
|
makeDicts( MPPARM(state->globalJNI->mpool) env, state->globalJNI->dictMgr,
|
||||||
globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths,
|
globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths,
|
||||||
jlang );
|
jlang );
|
||||||
|
@ -923,7 +1027,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
||||||
destroyDraw( &globals->dctx );
|
destroyDraw( &globals->dctx );
|
||||||
destroyXportProcs( &globals->xportProcs );
|
destroyXportProcs( &globals->xportProcs );
|
||||||
destroyUtil( &globals->util );
|
destroyUtil( &globals->util );
|
||||||
destroyJNIUtil( env, &globals->jniutil );
|
|
||||||
destroyGI( MPPARM(mpool) &globals->gi );
|
destroyGI( MPPARM(mpool) &globals->gi );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,7 +1659,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores
|
||||||
void
|
void
|
||||||
and_send_on_close( XWStreamCtxt* stream, void* closure )
|
and_send_on_close( XWStreamCtxt* stream, void* closure )
|
||||||
{
|
{
|
||||||
AndGlobals* globals = (AndGlobals*)closure;
|
AndGameGlobals* globals = (AndGameGlobals*)closure;
|
||||||
JNIState* state = (JNIState*)globals->state;
|
JNIState* state = (JNIState*)globals->state;
|
||||||
|
|
||||||
XP_ASSERT( !!state->game.comms );
|
XP_ASSERT( !!state->game.comms );
|
||||||
|
@ -2049,8 +2152,8 @@ JNIEXPORT jboolean JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_haveEnv
|
Java_org_eehouse_android_xw4_jni_XwJNI_haveEnv
|
||||||
( JNIEnv* env, jclass C, jint jniGlobalPtr )
|
( JNIEnv* env, jclass C, jint jniGlobalPtr )
|
||||||
{
|
{
|
||||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
jboolean result = NULL != prvEnvForMe(&state->ti);
|
jboolean result = NULL != prvEnvForMe(&globalState->ti);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2102,29 +2205,25 @@ static void freeIndices( DictIterData* data );
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
|
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
|
||||||
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, jstring jname,
|
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, jstring jname,
|
||||||
jstring jpath, jobject jniu )
|
jstring jpath )
|
||||||
{
|
{
|
||||||
jint closure = 0;
|
jint closure = 0;
|
||||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
|
|
||||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) env,
|
DictionaryCtxt* dict = makeDict( MPPARM(globalState->mpool) env,
|
||||||
&state->ti, jniu );
|
globalState->dictMgr, globalState->jniutil,
|
||||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
jname, jDictBytes, jpath, NULL, false );
|
||||||
jniutil, jname, jDictBytes, jpath, NULL,
|
|
||||||
false );
|
|
||||||
if ( !!dict ) {
|
if ( !!dict ) {
|
||||||
DictIterData* data = XP_CALLOC( state->mpool, sizeof(*data) );
|
DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
|
||||||
data->env = env;
|
data->env = env;
|
||||||
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(state->mpool) );
|
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globalState->mpool) );
|
||||||
data->jniutil = jniutil;
|
data->jniutil = globalState->jniutil;
|
||||||
data->dict = dict;
|
data->dict = dict;
|
||||||
data->depth = 2;
|
data->depth = 2;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
data->mpool = state->mpool;
|
data->mpool = globalState->mpool;
|
||||||
#endif
|
#endif
|
||||||
closure = (int)data;
|
closure = (int)data;
|
||||||
} else {
|
|
||||||
destroyJNIUtil( env, &jniutil );
|
|
||||||
}
|
}
|
||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
@ -2152,7 +2251,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dict_unref( data->dict );
|
dict_unref( data->dict );
|
||||||
destroyJNIUtil( env, &data->jniutil );
|
|
||||||
freeIndices( data );
|
freeIndices( data );
|
||||||
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
|
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
|
||||||
XP_FREE( mpool, data );
|
XP_FREE( mpool, data );
|
||||||
|
|
|
@ -53,7 +53,7 @@ git checkout ${TAG}${BRANCH}
|
||||||
cd ./xwords4/android/
|
cd ./xwords4/android/
|
||||||
./scripts/arelease.sh --apk-list $OUT_FILE
|
./scripts/arelease.sh --apk-list $OUT_FILE
|
||||||
mkdir -p /tmp/releases
|
mkdir -p /tmp/releases
|
||||||
cp app/build/outputs/apk/*.apk /tmp/releases
|
cp app/build/outputs/apk/xw4/release/*.apk /tmp/releases
|
||||||
|
|
||||||
if [ -n "$XW_RELEASE_SCP_DEST" ]; then
|
if [ -n "$XW_RELEASE_SCP_DEST" ]; then
|
||||||
cat $OUT_FILE | while read APK; do
|
cat $OUT_FILE | while read APK; do
|
||||||
|
|
|
@ -54,7 +54,7 @@ fi
|
||||||
|
|
||||||
if [ -z "$FILES" ]; then
|
if [ -z "$FILES" ]; then
|
||||||
do_build
|
do_build
|
||||||
for f in $(dirname $0)/../app/build/outputs/apk/*-unsigned-*.apk; do
|
for f in $(dirname $0)/../app/build/outputs/apk/xw4/release/*-unsigned-*.apk; do
|
||||||
$(dirname $0)/sign-align.sh --apk $f
|
$(dirname $0)/sign-align.sh --apk $f
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -166,6 +166,7 @@ board_make( MPFORMAL ModelCtxt* model, ServerCtxt* server, DrawCtx* draw,
|
||||||
|
|
||||||
result->draw = draw;
|
result->draw = draw;
|
||||||
result->util = util;
|
result->util = util;
|
||||||
|
result->dutil = util_getDevUtilCtxt( util );
|
||||||
result->gi = util->gameInfo;
|
result->gi = util->gameInfo;
|
||||||
XP_ASSERT( !!result->gi );
|
XP_ASSERT( !!result->gi );
|
||||||
|
|
||||||
|
@ -1090,11 +1091,10 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
|
||||||
XP_MEMSET( &bwl, 0, sizeof(bwl) );
|
XP_MEMSET( &bwl, 0, sizeof(bwl) );
|
||||||
|
|
||||||
if ( !legal ) {
|
if ( !legal ) {
|
||||||
stream = mem_stream_make( MPPARM(board->mpool)
|
stream = mem_stream_make_raw( MPPARM(board->mpool)
|
||||||
util_getVTManager(board->util), NULL,
|
dutil_getVTManager(board->dutil) );
|
||||||
CHANNEL_NONE, (MemStreamCloseCallback)NULL );
|
|
||||||
|
|
||||||
const XP_UCHAR* str = util_getUserString(board->util,
|
const XP_UCHAR* str = dutil_getUserString( board->dutil,
|
||||||
STR_COMMIT_CONFIRM );
|
STR_COMMIT_CONFIRM );
|
||||||
stream_catString( stream, str );
|
stream_catString( stream, str );
|
||||||
|
|
||||||
|
@ -1319,10 +1319,8 @@ timerFiredForPen( BoardCtxt* board )
|
||||||
NULL, NULL, NULL );
|
NULL, NULL, NULL );
|
||||||
if ( listWords ) {
|
if ( listWords ) {
|
||||||
XWStreamCtxt* stream =
|
XWStreamCtxt* stream =
|
||||||
mem_stream_make( MPPARM(board->mpool)
|
mem_stream_make_raw( MPPARM(board->mpool)
|
||||||
util_getVTManager(board->util), NULL,
|
dutil_getVTManager(board->dutil) );
|
||||||
CHANNEL_NONE,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
model_listWordsThrough( board->model, modelCol, modelRow,
|
model_listWordsThrough( board->model, modelCol, modelRow,
|
||||||
stream );
|
stream );
|
||||||
util_cellSquareHeld( board->util, stream );
|
util_cellSquareHeld( board->util, stream );
|
||||||
|
@ -1428,7 +1426,7 @@ board_pushTimerSave( BoardCtxt* board )
|
||||||
{
|
{
|
||||||
if ( board->gi->timerEnabled ) {
|
if ( board->gi->timerEnabled ) {
|
||||||
if ( board->timerSaveCount++ == 0 ) {
|
if ( board->timerSaveCount++ == 0 ) {
|
||||||
board->timerStoppedTime = util_getCurSeconds( board->util );
|
board->timerStoppedTime = dutil_getCurSeconds( board->dutil );
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
board->timerStoppedTurn = server_getCurrentTurn( board->server,
|
board->timerStoppedTurn = server_getCurrentTurn( board->server,
|
||||||
NULL );
|
NULL );
|
||||||
|
@ -1451,7 +1449,7 @@ board_popTimerSave( BoardCtxt* board )
|
||||||
XP_ASSERT( board->timerStoppedTurn == turn );
|
XP_ASSERT( board->timerStoppedTurn == turn );
|
||||||
|
|
||||||
if ( --board->timerSaveCount == 0 && turn >= 0 ) {
|
if ( --board->timerSaveCount == 0 && turn >= 0 ) {
|
||||||
XP_U32 curTime = util_getCurSeconds( board->util );
|
XP_U32 curTime = dutil_getCurSeconds( board->dutil );
|
||||||
XP_U32 elapsed;
|
XP_U32 elapsed;
|
||||||
|
|
||||||
XP_ASSERT( board->timerStoppedTime != 0 );
|
XP_ASSERT( board->timerStoppedTime != 0 );
|
||||||
|
|
|
@ -371,7 +371,7 @@ static XP_Bool
|
||||||
drawCell( BoardCtxt* board, const XP_U16 col, const XP_U16 row, XP_Bool skipBlanks )
|
drawCell( BoardCtxt* board, const XP_U16 col, const XP_U16 row, XP_Bool skipBlanks )
|
||||||
{
|
{
|
||||||
XP_Bool success = XP_TRUE;
|
XP_Bool success = XP_TRUE;
|
||||||
XP_Rect cellRect;
|
XP_Rect cellRect = {0};
|
||||||
Tile tile;
|
Tile tile;
|
||||||
XP_Bool isBlank, isEmpty, recent, pending = XP_FALSE;
|
XP_Bool isBlank, isEmpty, recent, pending = XP_FALSE;
|
||||||
XWBonusType bonus;
|
XWBonusType bonus;
|
||||||
|
|
|
@ -139,6 +139,7 @@ struct BoardCtxt {
|
||||||
ModelCtxt* model;
|
ModelCtxt* model;
|
||||||
ServerCtxt* server;
|
ServerCtxt* server;
|
||||||
DrawCtx* draw;
|
DrawCtx* draw;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
XW_UtilCtxt* util;
|
XW_UtilCtxt* util;
|
||||||
|
|
||||||
struct CurGameInfo* gi;
|
struct CurGameInfo* gi;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "comms.h"
|
#include "comms.h"
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "dutil.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "xwstream.h"
|
#include "xwstream.h"
|
||||||
#include "memstream.h"
|
#include "memstream.h"
|
||||||
|
@ -100,6 +101,7 @@ typedef struct AddressRecord {
|
||||||
|
|
||||||
struct CommsCtxt {
|
struct CommsCtxt {
|
||||||
XW_UtilCtxt* util;
|
XW_UtilCtxt* util;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
|
|
||||||
XP_U32 connID; /* set from gameID: 0 means ignore; otherwise
|
XP_U32 connID; /* set from gameID: 0 means ignore; otherwise
|
||||||
must match. Set by server. */
|
must match. Set by server. */
|
||||||
|
@ -353,8 +355,7 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CommsCtxt* comms = (CommsCtxt*)XP_MALLOC( mpool, sizeof(*comms) );
|
CommsCtxt* comms = (CommsCtxt*)XP_CALLOC( mpool, sizeof(*comms) );
|
||||||
XP_MEMSET( comms, 0, sizeof(*comms) );
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
comms->tag = mpool_getTag(mpool);
|
comms->tag = mpool_getTag(mpool);
|
||||||
XP_LOGF( TAGFMT(isServer=%d; forceChannel=%d), TAGPRMS, isServer, forceChannel );
|
XP_LOGF( TAGFMT(isServer=%d; forceChannel=%d), TAGPRMS, isServer, forceChannel );
|
||||||
|
@ -372,7 +373,9 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
|
||||||
comms->xportFlags = comms->procs.flags;
|
comms->xportFlags = comms->procs.flags;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
comms->dutil = util_getDevUtilCtxt( util );
|
||||||
comms->util = util;
|
comms->util = util;
|
||||||
|
comms->dutil = util_getDevUtilCtxt( util );
|
||||||
|
|
||||||
#ifdef XWFEATURE_RELAY
|
#ifdef XWFEATURE_RELAY
|
||||||
init_relay( comms, nPlayersHere, nPlayersTotal );
|
init_relay( comms, nPlayersHere, nPlayersTotal );
|
||||||
|
@ -722,7 +725,7 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
|
||||||
msg->msg = (XP_U8*)XP_MALLOC( mpool, msg->len );
|
msg->msg = (XP_U8*)XP_MALLOC( mpool, msg->len );
|
||||||
stream_getBytes( stream, msg->msg, msg->len );
|
stream_getBytes( stream, msg->msg, msg->len );
|
||||||
#ifdef COMMS_CHECKSUM
|
#ifdef COMMS_CHECKSUM
|
||||||
msg->checksum = util_md5sum( comms->util, msg->msg, msg->len );
|
msg->checksum = dutil_md5sum( comms->dutil, msg->msg, msg->len );
|
||||||
#endif
|
#endif
|
||||||
msg->next = (MsgQueueElem*)NULL;
|
msg->next = (MsgQueueElem*)NULL;
|
||||||
*prevsQueueNext = comms->msgQueueTail = msg;
|
*prevsQueueNext = comms->msgQueueTail = msg;
|
||||||
|
@ -802,6 +805,11 @@ sendConnect( CommsCtxt* comms, XP_Bool breakExisting )
|
||||||
(void)send_via_bt_or_ip( comms, BTIPMSG_RESET, CHANNEL_NONE, typ, NULL, 0, NULL );
|
(void)send_via_bt_or_ip( comms, BTIPMSG_RESET, CHANNEL_NONE, typ, NULL, 0, NULL );
|
||||||
(void)comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
|
(void)comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined XWFEATURE_SMS
|
||||||
|
case COMMS_CONN_SMS:
|
||||||
|
(void)comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -973,7 +981,6 @@ comms_setAddr( CommsCtxt* comms, const CommsAddrRec* addr )
|
||||||
setDoHeartbeat( comms );
|
setDoHeartbeat( comms );
|
||||||
#endif
|
#endif
|
||||||
sendConnect( comms, XP_TRUE );
|
sendConnect( comms, XP_TRUE );
|
||||||
|
|
||||||
} /* comms_setAddr */
|
} /* comms_setAddr */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1136,10 +1143,8 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
||||||
newMsgElem->sendCount = 0;
|
newMsgElem->sendCount = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
hdrStream = mem_stream_make( MPPARM(comms->mpool)
|
hdrStream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||||
util_getVTManager(comms->util),
|
dutil_getVTManager(comms->dutil));
|
||||||
NULL, 0,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
stream_open( hdrStream );
|
stream_open( hdrStream );
|
||||||
#if 0 < COMMS_VERSION
|
#if 0 < COMMS_VERSION
|
||||||
stream_putU16( hdrStream, HAS_VERSION_FLAG );
|
stream_putU16( hdrStream, HAS_VERSION_FLAG );
|
||||||
|
@ -1168,7 +1173,7 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COMMS_CHECKSUM
|
#ifdef COMMS_CHECKSUM
|
||||||
newMsgElem->checksum = util_md5sum( comms->util, newMsgElem->msg,
|
newMsgElem->checksum = dutil_md5sum( comms->dutil, newMsgElem->msg,
|
||||||
newMsgElem->len );
|
newMsgElem->len );
|
||||||
#endif
|
#endif
|
||||||
return newMsgElem;
|
return newMsgElem;
|
||||||
|
@ -1508,7 +1513,7 @@ comms_resendAll( CommsCtxt* comms, CommsConnType filter, XP_Bool force )
|
||||||
XP_Bool success = XP_TRUE;
|
XP_Bool success = XP_TRUE;
|
||||||
XP_ASSERT( !!comms );
|
XP_ASSERT( !!comms );
|
||||||
|
|
||||||
XP_U32 now = util_getCurSeconds( comms->util );
|
XP_U32 now = dutil_getCurSeconds( comms->dutil );
|
||||||
if ( !force && (now < comms->nextResend) ) {
|
if ( !force && (now < comms->nextResend) ) {
|
||||||
XP_LOGF( "%s: aborting: %d seconds left in backoff", __func__,
|
XP_LOGF( "%s: aborting: %d seconds left in backoff", __func__,
|
||||||
comms->nextResend - now );
|
comms->nextResend - now );
|
||||||
|
@ -1664,7 +1669,7 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
}
|
}
|
||||||
if ( ID_TYPE_NONE == typ /* error case */
|
if ( ID_TYPE_NONE == typ /* error case */
|
||||||
|| '\0' != devID[0] ) /* new info case */ {
|
|| '\0' != devID[0] ) /* new info case */ {
|
||||||
util_deviceRegistered( comms->util, typ, devID );
|
dutil_deviceRegistered( comms->dutil, typ, devID );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1845,7 +1850,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
||||||
static void
|
static void
|
||||||
noteHBReceived( CommsCtxt* comms/* , const CommsAddrRec* addr */ )
|
noteHBReceived( CommsCtxt* comms/* , const CommsAddrRec* addr */ )
|
||||||
{
|
{
|
||||||
comms->lastMsgRcvdTime = util_getCurSeconds( comms->util );
|
comms->lastMsgRcvdTime = dutil_getCurSeconds( comms->dutil );
|
||||||
setHeartbeatTimer( comms );
|
setHeartbeatTimer( comms );
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -1976,12 +1981,15 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
|
||||||
break;
|
break;
|
||||||
case COMMS_CONN_SMS:
|
case COMMS_CONN_SMS:
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
if ( util_phoneNumbersSame( comms->util, addr->u.sms.phone,
|
{
|
||||||
|
XW_DUtilCtxt* duc = util_getDevUtilCtxt( comms->util );
|
||||||
|
if ( dutil_phoneNumbersSame( duc, addr->u.sms.phone,
|
||||||
rec->addr.u.sms.phone )
|
rec->addr.u.sms.phone )
|
||||||
&& addr->u.sms.port == rec->addr.u.sms.port ) {
|
&& addr->u.sms.port == rec->addr.u.sms.port ) {
|
||||||
matched = XP_TRUE;
|
matched = XP_TRUE;
|
||||||
XP_ASSERT( 0 );
|
XP_ASSERT( 0 );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case COMMS_CONN_NONE:
|
case COMMS_CONN_NONE:
|
||||||
|
@ -2227,7 +2235,7 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
XP_U16 len = stream_getSize( stream );
|
XP_U16 len = stream_getSize( stream );
|
||||||
// stream_getPtr pts at base, but sum excludes relay header
|
// stream_getPtr pts at base, but sum excludes relay header
|
||||||
const XP_U8* ptr = initialLen - len + stream_getPtr( stream );
|
const XP_U8* ptr = initialLen - len + stream_getPtr( stream );
|
||||||
XP_UCHAR* sum = util_md5sum( comms->util, ptr, len );
|
XP_UCHAR* sum = dutil_md5sum( comms->dutil, ptr, len );
|
||||||
XP_LOGF( TAGFMT() "got message of len %d with sum %s",
|
XP_LOGF( TAGFMT() "got message of len %d with sum %s",
|
||||||
TAGPRMS, len, sum );
|
TAGPRMS, len, sum );
|
||||||
XP_FREE( comms->mpool, sum );
|
XP_FREE( comms->mpool, sum );
|
||||||
|
@ -2433,7 +2441,7 @@ heartbeat_checks( CommsCtxt* comms )
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if ( comms->lastMsgRcvdTime > 0 ) {
|
if ( comms->lastMsgRcvdTime > 0 ) {
|
||||||
XP_U32 now = util_getCurSeconds( comms->util );
|
XP_U32 now = dutil_getCurSeconds( comms->dutil );
|
||||||
XP_U32 tooLongAgo = now - (HB_INTERVAL * 2);
|
XP_U32 tooLongAgo = now - (HB_INTERVAL * 2);
|
||||||
if ( comms->lastMsgRcvdTime < tooLongAgo ) {
|
if ( comms->lastMsgRcvdTime < tooLongAgo ) {
|
||||||
XP_LOGF( "%s: calling reset proc; last was %ld secs too long "
|
XP_LOGF( "%s: calling reset proc; last was %ld secs too long "
|
||||||
|
@ -2637,10 +2645,8 @@ logAddr( const CommsCtxt* comms, const CommsAddrRec* addr, const char* caller )
|
||||||
{
|
{
|
||||||
if ( !!addr ) {
|
if ( !!addr ) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(comms->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||||
util_getVTManager(comms->util),
|
dutil_getVTManager(comms->dutil));
|
||||||
NULL, 0,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
snprintf( buf, sizeof(buf), TAGFMT() "called on %p from %s:\n", TAGPRMS,
|
snprintf( buf, sizeof(buf), TAGFMT() "called on %p from %s:\n", TAGPRMS,
|
||||||
addr, caller );
|
addr, caller );
|
||||||
stream_catString( stream, buf );
|
stream_catString( stream, buf );
|
||||||
|
@ -2942,10 +2948,8 @@ relay_msg_to_stream( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID,
|
||||||
{
|
{
|
||||||
XP_LOGF( "%s(cmd=%s, destID=%x)", __func__, relayCmdToStr(cmd), destID );
|
XP_LOGF( "%s(cmd=%s, destID=%x)", __func__, relayCmdToStr(cmd), destID );
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
stream = mem_stream_make( MPPARM(comms->mpool)
|
stream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||||
util_getVTManager(comms->util),
|
dutil_getVTManager(comms->dutil) );
|
||||||
NULL, 0,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
if ( stream != NULL ) {
|
if ( stream != NULL ) {
|
||||||
CommsAddrRec addr;
|
CommsAddrRec addr;
|
||||||
stream_open( stream );
|
stream_open( stream );
|
||||||
|
@ -3200,7 +3204,7 @@ putDevID( const CommsCtxt* comms, XWStreamCtxt* stream )
|
||||||
{
|
{
|
||||||
# if XWRELAY_PROTO_VERSION >= XWRELAY_PROTO_VERSION_CLIENTID
|
# if XWRELAY_PROTO_VERSION >= XWRELAY_PROTO_VERSION_CLIENTID
|
||||||
DevIDType typ;
|
DevIDType typ;
|
||||||
const XP_UCHAR* devID = util_getDevID( comms->util, &typ );
|
const XP_UCHAR* devID = dutil_getDevID( comms->dutil, &typ );
|
||||||
XP_ASSERT( ID_TYPE_NONE <= typ && typ < ID_TYPE_NTYPES );
|
XP_ASSERT( ID_TYPE_NONE <= typ && typ < ID_TYPE_NTYPES );
|
||||||
stream_putU8( stream, typ );
|
stream_putU8( stream, typ );
|
||||||
if ( ID_TYPE_NONE != typ ) {
|
if ( ID_TYPE_NONE != typ ) {
|
||||||
|
|
|
@ -129,6 +129,22 @@ typedef enum {
|
||||||
OBJ_TRAY
|
OBJ_TRAY
|
||||||
} BoardObjectType;
|
} BoardObjectType;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SERVER_STANDALONE,
|
||||||
|
SERVER_ISSERVER,
|
||||||
|
SERVER_ISCLIENT
|
||||||
|
};
|
||||||
|
typedef XP_U8 DeviceRole;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PHONIES_IGNORE,
|
||||||
|
PHONIES_WARN,
|
||||||
|
PHONIES_DISALLOW
|
||||||
|
};
|
||||||
|
typedef XP_U8 XWPhoniesChoice;
|
||||||
|
|
||||||
|
typedef XP_U8 XP_LangCode;
|
||||||
|
|
||||||
/* I'm going to try putting all forward "class" decls in the same file */
|
/* I'm going to try putting all forward "class" decls in the same file */
|
||||||
typedef struct BoardCtxt BoardCtxt;
|
typedef struct BoardCtxt BoardCtxt;
|
||||||
typedef struct CommMgrCtxt CommMgrCtxt;
|
typedef struct CommMgrCtxt CommMgrCtxt;
|
||||||
|
@ -144,6 +160,7 @@ typedef struct XWStreamCtxt XWStreamCtxt;
|
||||||
typedef struct TrayContext TrayContext;
|
typedef struct TrayContext TrayContext;
|
||||||
typedef struct PoolContext PoolContext;
|
typedef struct PoolContext PoolContext;
|
||||||
typedef struct XW_UtilCtxt XW_UtilCtxt;
|
typedef struct XW_UtilCtxt XW_UtilCtxt;
|
||||||
|
typedef struct XW_DUtilCtxt XW_DUtilCtxt;
|
||||||
|
|
||||||
/* Low two bits treated as channel, third as short-term flag indicating
|
/* Low two bits treated as channel, third as short-term flag indicating
|
||||||
* sender's role; rest can be random to aid detection of duplicate packets. */
|
* sender's role; rest can be random to aid detection of duplicate packets. */
|
||||||
|
@ -191,6 +208,8 @@ typedef enum {
|
||||||
BONUS_LAST
|
BONUS_LAST
|
||||||
} XWBonusType;
|
} XWBonusType;
|
||||||
|
|
||||||
|
#define PERSIST_KEY(str) __FILE__ ":" str
|
||||||
|
|
||||||
/* I need a way to communiate prefs to common/ code. For now, though, I'll
|
/* I need a way to communiate prefs to common/ code. For now, though, I'll
|
||||||
* leave storage of these values up to the platforms. First, because I don't
|
* leave storage of these values up to the platforms. First, because I don't
|
||||||
* want to deal with versioning in the common code. Second, becuase they
|
* want to deal with versioning in the common code. Second, becuase they
|
||||||
|
|
|
@ -48,6 +48,7 @@ COMMONSRC = \
|
||||||
$(COMMONDIR)/vtabmgr.c \
|
$(COMMONDIR)/vtabmgr.c \
|
||||||
$(COMMONDIR)/dictmgr.c \
|
$(COMMONDIR)/dictmgr.c \
|
||||||
$(COMMONDIR)/dbgutil.c \
|
$(COMMONDIR)/dbgutil.c \
|
||||||
|
$(COMMONDIR)/smsproto.c \
|
||||||
|
|
||||||
# PENDING: define this in terms of above!!!
|
# PENDING: define this in terms of above!!!
|
||||||
|
|
||||||
|
@ -85,5 +86,6 @@ COMMON5 = \
|
||||||
$(COMMONOBJDIR)/vtabmgr.o \
|
$(COMMONOBJDIR)/vtabmgr.o \
|
||||||
$(COMMONOBJDIR)/dictmgr.o \
|
$(COMMONOBJDIR)/dictmgr.o \
|
||||||
$(COMMONOBJDIR)/dbgutil.o \
|
$(COMMONOBJDIR)/dbgutil.o \
|
||||||
|
$(COMMONOBJDIR)/smsproto.o \
|
||||||
|
|
||||||
COMMONOBJ = $(COMMON1) $(COMMON2) $(COMMON3) $(COMMON4) $(COMMON5)
|
COMMONOBJ = $(COMMON1) $(COMMON2) $(COMMON3) $(COMMON4) $(COMMON5)
|
||||||
|
|
|
@ -44,8 +44,6 @@ extern "C" {
|
||||||
#define DICT_HEADER_MASK 0x08
|
#define DICT_HEADER_MASK 0x08
|
||||||
#define DICT_SYNONYMS_MASK 0x10
|
#define DICT_SYNONYMS_MASK 0x10
|
||||||
|
|
||||||
typedef XP_U8 XP_LangCode;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INTRADE_MW_TEXT = BONUS_LAST
|
INTRADE_MW_TEXT = BONUS_LAST
|
||||||
} XWMiniTextType;
|
} XWMiniTextType;
|
||||||
|
|
104
xwords4/common/dutil.h
Normal file
104
xwords4/common/dutil.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DEVUTIL_H_
|
||||||
|
#define _DEVUTIL_H_
|
||||||
|
|
||||||
|
#include "mempool.h"
|
||||||
|
#include "comtypes.h"
|
||||||
|
#include "xwrelay.h"
|
||||||
|
#include "vtabmgr.h"
|
||||||
|
|
||||||
|
typedef struct _DUtilVtable {
|
||||||
|
XP_U32 (*m_dutil_getCurSeconds)( XW_DUtilCtxt* duc );
|
||||||
|
const XP_UCHAR* (*m_dutil_getUserString)( XW_DUtilCtxt* duc,
|
||||||
|
XP_U16 stringCode );
|
||||||
|
const XP_UCHAR* (*m_dutil_getUserQuantityString)( XW_DUtilCtxt* duc,
|
||||||
|
XP_U16 stringCode,
|
||||||
|
XP_U16 quantity );
|
||||||
|
void (*m_dutil_storeStream)( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* data );
|
||||||
|
/* Pass in an empty stream, and it'll be returned full */
|
||||||
|
void (*m_dutil_loadStream)( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* inOut );
|
||||||
|
void (*m_dutil_storePtr)( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
const void* data, XP_U16 len );
|
||||||
|
void (*m_dutil_loadPtr)( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
void* data, XP_U16* lenp );
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
XP_Bool (*m_dutil_phoneNumbersSame)( XW_DUtilCtxt* uc, const XP_UCHAR* p1,
|
||||||
|
const XP_UCHAR* p2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
const XP_UCHAR* (*m_dutil_getDevID)( XW_DUtilCtxt* duc, DevIDType* typ );
|
||||||
|
void (*m_dutil_deviceRegistered)( XW_DUtilCtxt* duc, DevIDType typ,
|
||||||
|
const XP_UCHAR* idRelay );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
XP_UCHAR* (*m_dutil_md5sum)( XW_DUtilCtxt* duc, const XP_U8* ptr, XP_U16 len );
|
||||||
|
#endif
|
||||||
|
} DUtilVtable;
|
||||||
|
|
||||||
|
struct XW_DUtilCtxt {
|
||||||
|
DUtilVtable vtable;
|
||||||
|
|
||||||
|
void* closure;
|
||||||
|
VTableMgr* vtMgr;
|
||||||
|
MPSLOT
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This one cheats: direct access */
|
||||||
|
#define dutil_getVTManager(duc) (duc)->vtMgr
|
||||||
|
|
||||||
|
#define dutil_getCurSeconds(duc) \
|
||||||
|
(duc)->vtable.m_dutil_getCurSeconds((duc))
|
||||||
|
#define dutil_getUserString( duc, c ) \
|
||||||
|
(duc)->vtable.m_dutil_getUserString((duc),(c))
|
||||||
|
#define dutil_getUserQuantityString( duc, c, q ) \
|
||||||
|
(duc)->vtable.m_dutil_getUserQuantityString((duc),(c),(q))
|
||||||
|
|
||||||
|
#define dutil_storeStream(duc, k, s) \
|
||||||
|
(duc)->vtable.m_dutil_storeStream((duc), (k), (s));
|
||||||
|
#define dutil_storePtr(duc, k, p, l) \
|
||||||
|
(duc)->vtable.m_dutil_storePtr((duc), (k), (p), (l));
|
||||||
|
#define dutil_loadStream(duc, k, s) \
|
||||||
|
(duc)->vtable.m_dutil_loadStream((duc), (k), (s));
|
||||||
|
#define dutil_loadPtr(duc, k, p, l) \
|
||||||
|
(duc)->vtable.m_dutil_loadPtr((duc), (k), (p), (l));
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
# define dutil_phoneNumbersSame(duc,p1,p2) \
|
||||||
|
(duc)->vtable.m_dutil_phoneNumbersSame( (duc), (p1), (p2) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
# define dutil_getDevID( duc, t ) \
|
||||||
|
(duc)->vtable.m_dutil_getDevID((duc),(t))
|
||||||
|
# define dutil_deviceRegistered( duc, typ, id ) \
|
||||||
|
(duc)->vtable.m_dutil_deviceRegistered( (duc), (typ), (id) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
# define dutil_md5sum( duc, p, l ) (duc)->vtable.m_dutil_md5sum((duc), (p), (l))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -80,7 +80,7 @@ makeGameID( XW_UtilCtxt* util )
|
||||||
XP_U32 gameID = 0;
|
XP_U32 gameID = 0;
|
||||||
assertUtilOK( util );
|
assertUtilOK( util );
|
||||||
while ( 0 == gameID ) {
|
while ( 0 == gameID ) {
|
||||||
gameID = util_getCurSeconds( util );
|
gameID = dutil_getCurSeconds( util_getDevUtilCtxt( util ) );
|
||||||
}
|
}
|
||||||
return gameID;
|
return gameID;
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,11 @@ void*
|
||||||
mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
|
mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
|
||||||
const char* func, XP_U32 lineNo )
|
const char* func, XP_U32 lineNo )
|
||||||
{
|
{
|
||||||
|
// XP_LOGF( "%s(func=%s, line=%d): newsize: %d", __func__, func, lineNo, newsize );
|
||||||
|
void* result;
|
||||||
|
if ( ptr == NULL ) {
|
||||||
|
result = mpool_alloc( mpool, newsize, file, func, lineNo );
|
||||||
|
} else {
|
||||||
MemPoolEntry* entry = findEntryFor( mpool, ptr, (MemPoolEntry**)NULL );
|
MemPoolEntry* entry = findEntryFor( mpool, ptr, (MemPoolEntry**)NULL );
|
||||||
|
|
||||||
if ( !entry ) {
|
if ( !entry ) {
|
||||||
|
@ -271,7 +276,9 @@ mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
|
||||||
entry->lineNo = lineNo;
|
entry->lineNo = lineNo;
|
||||||
entry->size = newsize;
|
entry->size = newsize;
|
||||||
}
|
}
|
||||||
return entry->ptr;
|
result = entry->ptr;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
} /* mpool_realloc */
|
} /* mpool_realloc */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -66,6 +66,12 @@ static StreamCtxVTable* make_vtable( MemStreamCtxt* stream );
|
||||||
/* Try to keep this the only entry point to this file, and to keep it at the
|
/* Try to keep this the only entry point to this file, and to keep it at the
|
||||||
* top of the file (first executable code).
|
* top of the file (first executable code).
|
||||||
*/
|
*/
|
||||||
|
XWStreamCtxt*
|
||||||
|
mem_stream_make_raw( MPFORMAL VTableMgr* vtmgr )
|
||||||
|
{
|
||||||
|
return mem_stream_make( MPPARM(mpool) vtmgr, NULL, 0, NULL );
|
||||||
|
}
|
||||||
|
|
||||||
XWStreamCtxt*
|
XWStreamCtxt*
|
||||||
mem_stream_make( MPFORMAL VTableMgr* vtmgr, void* closure,
|
mem_stream_make( MPFORMAL VTableMgr* vtmgr, void* closure,
|
||||||
XP_PlayerAddr channelNo, MemStreamCloseCallback onClose )
|
XP_PlayerAddr channelNo, MemStreamCloseCallback onClose )
|
||||||
|
|
|
@ -32,6 +32,8 @@ extern "C" {
|
||||||
typedef void (*MemStreamCloseCallback)( XWStreamCtxt* stream,
|
typedef void (*MemStreamCloseCallback)( XWStreamCtxt* stream,
|
||||||
void* closure );
|
void* closure );
|
||||||
|
|
||||||
|
XWStreamCtxt* mem_stream_make_raw( MPFORMAL VTableMgr* vtmgr);
|
||||||
|
|
||||||
XWStreamCtxt* mem_stream_make( MPFORMAL VTableMgr* vtmgr,
|
XWStreamCtxt* mem_stream_make( MPFORMAL VTableMgr* vtmgr,
|
||||||
void* closure,
|
void* closure,
|
||||||
XP_PlayerAddr addr, /* should be in a
|
XP_PlayerAddr addr, /* should be in a
|
||||||
|
|
|
@ -108,6 +108,7 @@ model_make( MPFORMAL DictionaryCtxt* dict, const PlayerDicts* dicts,
|
||||||
MPASSIGN(result->vol.mpool, mpool);
|
MPASSIGN(result->vol.mpool, mpool);
|
||||||
|
|
||||||
result->vol.util = util;
|
result->vol.util = util;
|
||||||
|
result->vol.dutil = util_getDevUtilCtxt( util );
|
||||||
result->vol.wni.proc = recordWord;
|
result->vol.wni.proc = recordWord;
|
||||||
result->vol.wni.closure = &result->vol.rwi;
|
result->vol.wni.closure = &result->vol.rwi;
|
||||||
|
|
||||||
|
@ -295,7 +296,7 @@ model_setSize( ModelCtxt* model, XP_U16 nCols )
|
||||||
stack_init( model->vol.stack );
|
stack_init( model->vol.stack );
|
||||||
} else {
|
} else {
|
||||||
model->vol.stack = stack_make( MPPARM(model->vol.mpool)
|
model->vol.stack = stack_make( MPPARM(model->vol.mpool)
|
||||||
util_getVTManager(model->vol.util));
|
dutil_getVTManager(model->vol.dutil));
|
||||||
}
|
}
|
||||||
} /* model_setSize */
|
} /* model_setSize */
|
||||||
|
|
||||||
|
@ -2090,13 +2091,13 @@ printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isPass ) {
|
if ( isPass ) {
|
||||||
format = util_getUserString( model->vol.util, STR_PASS );
|
format = dutil_getUserString( model->vol.dutil, STR_PASS );
|
||||||
XP_SNPRINTF( buf, VSIZE(buf), "%s", format );
|
XP_SNPRINTF( buf, VSIZE(buf), "%s", format );
|
||||||
} else {
|
} else {
|
||||||
if ( isHorizontal ) {
|
if ( isHorizontal ) {
|
||||||
format = util_getUserString( model->vol.util, STRS_MOVE_ACROSS );
|
format = dutil_getUserString( model->vol.dutil, STRS_MOVE_ACROSS );
|
||||||
} else {
|
} else {
|
||||||
format = util_getUserString( model->vol.util, STRS_MOVE_DOWN );
|
format = dutil_getUserString( model->vol.dutil, STRS_MOVE_DOWN );
|
||||||
}
|
}
|
||||||
|
|
||||||
row = mi->commonCoord;
|
row = mi->commonCoord;
|
||||||
|
@ -2114,7 +2115,7 @@ printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !closure->keepHidden ) {
|
if ( !closure->keepHidden ) {
|
||||||
format = util_getUserString( model->vol.util, STRS_TRAY_AT_START );
|
format = dutil_getUserString( model->vol.dutil, STRS_TRAY_AT_START );
|
||||||
formatTray( model_getPlayerTiles( model, entry->playerNum ),
|
formatTray( model_getPlayerTiles( model, entry->playerNum ),
|
||||||
closure->dict, (XP_UCHAR*)traybuf, sizeof(traybuf),
|
closure->dict, (XP_UCHAR*)traybuf, sizeof(traybuf),
|
||||||
XP_FALSE );
|
XP_FALSE );
|
||||||
|
@ -2149,17 +2150,17 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
|
||||||
formatTray( (const TrayTileSet*) &entry->u.trade.newTiles,
|
formatTray( (const TrayTileSet*) &entry->u.trade.newTiles,
|
||||||
dict, traybuf2, sizeof(traybuf2), closure->keepHidden );
|
dict, traybuf2, sizeof(traybuf2), closure->keepHidden );
|
||||||
|
|
||||||
format = util_getUserString( model->vol.util, STRSS_TRADED_FOR );
|
format = dutil_getUserString( model->vol.dutil, STRSS_TRADED_FOR );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), format, traybuf1, traybuf2 );
|
XP_SNPRINTF( buf, sizeof(buf), format, traybuf1, traybuf2 );
|
||||||
printString( stream, buf );
|
printString( stream, buf );
|
||||||
printString( stream, (XP_UCHAR*)XP_CR );
|
printString( stream, (XP_UCHAR*)XP_CR );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PHONY_TYPE:
|
case PHONY_TYPE:
|
||||||
format = util_getUserString( model->vol.util, STR_PHONY_REJECTED );
|
format = dutil_getUserString( model->vol.dutil, STR_PHONY_REJECTED );
|
||||||
printString( stream, format );
|
printString( stream, format );
|
||||||
case MOVE_TYPE:
|
case MOVE_TYPE:
|
||||||
format = util_getUserString( model->vol.util, STRD_CUMULATIVE_SCORE );
|
format = dutil_getUserString( model->vol.dutil, STRD_CUMULATIVE_SCORE );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), format, totalScore );
|
XP_SNPRINTF( buf, sizeof(buf), format, totalScore );
|
||||||
printString( stream, buf );
|
printString( stream, buf );
|
||||||
|
|
||||||
|
@ -2174,7 +2175,7 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
|
||||||
if ( entry->moveType == PHONY_TYPE ) {
|
if ( entry->moveType == PHONY_TYPE ) {
|
||||||
/* printString( stream, (XP_UCHAR*)"phony rejected " ); */
|
/* printString( stream, (XP_UCHAR*)"phony rejected " ); */
|
||||||
} else if ( !closure->keepHidden ) {
|
} else if ( !closure->keepHidden ) {
|
||||||
format = util_getUserString(model->vol.util, STRS_NEW_TILES);
|
format = dutil_getUserString( model->vol.dutil, STRS_NEW_TILES );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), format,
|
XP_SNPRINTF( buf, sizeof(buf), format,
|
||||||
formatTray( &entry->u.move.newTiles, dict,
|
formatTray( &entry->u.move.newTiles, dict,
|
||||||
traybuf1, sizeof(traybuf1),
|
traybuf1, sizeof(traybuf1),
|
||||||
|
@ -2195,9 +2196,9 @@ static void
|
||||||
copyStack( const ModelCtxt* model, StackCtxt* destStack,
|
copyStack( const ModelCtxt* model, StackCtxt* destStack,
|
||||||
const StackCtxt* srcStack )
|
const StackCtxt* srcStack )
|
||||||
{
|
{
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(model->vol.mpool)
|
XWStreamCtxt* stream =
|
||||||
util_getVTManager(model->vol.util),
|
mem_stream_make_raw( MPPARM(model->vol.mpool)
|
||||||
NULL, 0, NULL );
|
dutil_getVTManager(model->vol.dutil) );
|
||||||
|
|
||||||
stack_writeToStream( (StackCtxt*)srcStack, stream );
|
stack_writeToStream( (StackCtxt*)srcStack, stream );
|
||||||
stack_loadFromStream( destStack, stream );
|
stack_loadFromStream( destStack, stream );
|
||||||
|
|
|
@ -51,6 +51,7 @@ typedef struct _RecordWordsInfo {
|
||||||
} RecordWordsInfo;
|
} RecordWordsInfo;
|
||||||
|
|
||||||
typedef struct ModelVolatiles {
|
typedef struct ModelVolatiles {
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
XW_UtilCtxt* util;
|
XW_UtilCtxt* util;
|
||||||
struct CurGameInfo* gi;
|
struct CurGameInfo* gi;
|
||||||
DictionaryCtxt* dict;
|
DictionaryCtxt* dict;
|
||||||
|
|
|
@ -120,9 +120,7 @@ stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream )
|
||||||
stack->highWaterMark = stream_getU16( stream );
|
stack->highWaterMark = stream_getU16( stream );
|
||||||
stack->nEntries = stream_getU16( stream );
|
stack->nEntries = stream_getU16( stream );
|
||||||
stack->top = stream_getU32( stream );
|
stack->top = stream_getU32( stream );
|
||||||
stack->data = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr,
|
stack->data = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
|
||||||
NULL, 0,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
|
|
||||||
stream_getFromStream( stack->data, stream, nBytes );
|
stream_getFromStream( stack->data, stream, nBytes );
|
||||||
} else {
|
} else {
|
||||||
|
@ -164,8 +162,8 @@ StackCtxt*
|
||||||
stack_copy( const StackCtxt* stack )
|
stack_copy( const StackCtxt* stack )
|
||||||
{
|
{
|
||||||
StackCtxt* newStack = NULL;
|
StackCtxt* newStack = NULL;
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(stack->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(stack->mpool)
|
||||||
stack->vtmgr, NULL, 0, NULL );
|
stack->vtmgr );
|
||||||
stack_writeToStream( stack, stream );
|
stack_writeToStream( stack, stream );
|
||||||
|
|
||||||
newStack = stack_make( MPPARM(stack->mpool) stack->vtmgr );
|
newStack = stack_make( MPPARM(stack->mpool) stack->vtmgr );
|
||||||
|
@ -184,8 +182,7 @@ pushEntryImpl( StackCtxt* stack, const StackEntry* entry )
|
||||||
XWStreamCtxt* stream = stack->data;
|
XWStreamCtxt* stream = stack->data;
|
||||||
|
|
||||||
if ( !stream ) {
|
if ( !stream ) {
|
||||||
stream = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr, NULL, 0,
|
stream = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
stack->data = stream;
|
stack->data = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -513,7 +513,7 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
|
||||||
score += EMPTIED_TRAY_BONUS;
|
score += EMPTIED_TRAY_BONUS;
|
||||||
|
|
||||||
if ( !!stream ) {
|
if ( !!stream ) {
|
||||||
const XP_UCHAR* bstr = util_getUserString( model->vol.util,
|
const XP_UCHAR* bstr = dutil_getUserString( model->vol.dutil,
|
||||||
STR_BONUS_ALL );
|
STR_BONUS_ALL );
|
||||||
stream_catString( stream, bstr );
|
stream_catString( stream, bstr );
|
||||||
}
|
}
|
||||||
|
@ -845,7 +845,7 @@ formatSummary( XWStreamCtxt* stream, const ModelCtxt* model, XP_U16 score )
|
||||||
{
|
{
|
||||||
XP_UCHAR buf[60];
|
XP_UCHAR buf[60];
|
||||||
XP_SNPRINTF( buf, sizeof(buf),
|
XP_SNPRINTF( buf, sizeof(buf),
|
||||||
util_getUserString(model->vol.util, STRD_TURN_SCORE),
|
dutil_getUserString(model->vol.dutil, STRD_TURN_SCORE),
|
||||||
score );
|
score );
|
||||||
XP_ASSERT( XP_STRLEN(buf) < sizeof(buf) );
|
XP_ASSERT( XP_STRLEN(buf) < sizeof(buf) );
|
||||||
stream_catString( stream, buf );
|
stream_catString( stream, buf );
|
||||||
|
|
|
@ -531,7 +531,7 @@ setRoleStrings( NewGameCtx* ngc )
|
||||||
strID = STR_TOTALPLAYERS;
|
strID = STR_TOTALPLAYERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ng_cp = util_getUserString( ngc->util, strID );
|
value.ng_cp = dutil_getUserString( util_getDevUtilCtxt(ngc->util), strID );
|
||||||
(*ngc->setAttrProc)( closure, NG_ATTR_NPLAYHEADER, value );
|
(*ngc->setAttrProc)( closure, NG_ATTR_NPLAYHEADER, value );
|
||||||
} /* setRoleStrings */
|
} /* setRoleStrings */
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ typedef struct ServerVolatiles {
|
||||||
ModelCtxt* model;
|
ModelCtxt* model;
|
||||||
CommsCtxt* comms;
|
CommsCtxt* comms;
|
||||||
XW_UtilCtxt* util;
|
XW_UtilCtxt* util;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
CurGameInfo* gi;
|
CurGameInfo* gi;
|
||||||
TurnChangeListener turnChangeListener;
|
TurnChangeListener turnChangeListener;
|
||||||
void* turnChangeData;
|
void* turnChangeData;
|
||||||
|
@ -271,6 +272,7 @@ server_make( MPFORMAL ModelCtxt* model, CommsCtxt* comms, XW_UtilCtxt* util )
|
||||||
result->vol.model = model;
|
result->vol.model = model;
|
||||||
result->vol.comms = comms;
|
result->vol.comms = comms;
|
||||||
result->vol.util = util;
|
result->vol.util = util;
|
||||||
|
result->vol.dutil = util_getDevUtilCtxt( util );
|
||||||
result->vol.gi = util->gameInfo;
|
result->vol.gi = util->gameInfo;
|
||||||
|
|
||||||
initServer( result );
|
initServer( result );
|
||||||
|
@ -672,7 +674,7 @@ sendChatToClientsExcept( ServerCtxt* server, XP_U16 skip, const XP_UCHAR* msg,
|
||||||
void
|
void
|
||||||
server_sendChat( ServerCtxt* server, const XP_UCHAR* msg, XP_S16 from )
|
server_sendChat( ServerCtxt* server, const XP_UCHAR* msg, XP_S16 from )
|
||||||
{
|
{
|
||||||
XP_U32 timestamp = util_getCurSeconds( server->vol.util );
|
XP_U32 timestamp = dutil_getCurSeconds( server->vol.dutil );
|
||||||
if ( server->vol.gi->serverRole == SERVER_ISCLIENT ) {
|
if ( server->vol.gi->serverRole == SERVER_ISCLIENT ) {
|
||||||
sendChatTo( server, SERVER_DEVICE, msg, from, timestamp );
|
sendChatTo( server, SERVER_DEVICE, msg, from, timestamp );
|
||||||
} else {
|
} else {
|
||||||
|
@ -828,10 +830,8 @@ static XWStreamCtxt*
|
||||||
mkServerStream( ServerCtxt* server )
|
mkServerStream( ServerCtxt* server )
|
||||||
{
|
{
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
stream = mem_stream_make( MPPARM(server->mpool)
|
stream = mem_stream_make_raw( MPPARM(server->mpool)
|
||||||
util_getVTManager(server->vol.util),
|
dutil_getVTManager(server->vol.dutil) );
|
||||||
NULL, CHANNEL_NONE,
|
|
||||||
(MemStreamCloseCallback)NULL );
|
|
||||||
XP_ASSERT( !!stream );
|
XP_ASSERT( !!stream );
|
||||||
return stream;
|
return stream;
|
||||||
} /* mkServerStream */
|
} /* mkServerStream */
|
||||||
|
@ -849,11 +849,11 @@ makeRobotMove( ServerCtxt* server )
|
||||||
XP_Bool timerEnabled = gi->timerEnabled;
|
XP_Bool timerEnabled = gi->timerEnabled;
|
||||||
XP_Bool canMove;
|
XP_Bool canMove;
|
||||||
XP_U32 time = 0L; /* stupid compiler.... */
|
XP_U32 time = 0L; /* stupid compiler.... */
|
||||||
XW_UtilCtxt* util = server->vol.util;
|
XW_DUtilCtxt* dutil = server->vol.dutil;
|
||||||
XP_Bool forceTrade = XP_FALSE;
|
XP_Bool forceTrade = XP_FALSE;
|
||||||
|
|
||||||
if ( timerEnabled ) {
|
if ( timerEnabled ) {
|
||||||
time = util_getCurSeconds( util );
|
time = dutil_getCurSeconds( dutil );
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef XWFEATURE_SLOW_ROBOT
|
#ifdef XWFEATURE_SLOW_ROBOT
|
||||||
|
@ -917,7 +917,7 @@ makeRobotMove( ServerCtxt* server )
|
||||||
|
|
||||||
if ( !!stream ) {
|
if ( !!stream ) {
|
||||||
XP_UCHAR buf[64];
|
XP_UCHAR buf[64];
|
||||||
str = util_getUserQuantityString( util, STRD_ROBOT_TRADED,
|
str = dutil_getUserQuantityString( dutil, STRD_ROBOT_TRADED,
|
||||||
MAX_TRAY_TILES );
|
MAX_TRAY_TILES );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), str, MAX_TRAY_TILES );
|
XP_SNPRINTF( buf, sizeof(buf), str, MAX_TRAY_TILES );
|
||||||
|
|
||||||
|
@ -951,7 +951,7 @@ makeRobotMove( ServerCtxt* server )
|
||||||
|
|
||||||
if ( timerEnabled ) {
|
if ( timerEnabled ) {
|
||||||
gi->players[turn].secondsUsed +=
|
gi->players[turn].secondsUsed +=
|
||||||
(XP_U16)(util_getCurSeconds( util ) - time);
|
(XP_U16)(dutil_getCurSeconds( dutil ) - time);
|
||||||
} else {
|
} else {
|
||||||
XP_ASSERT( gi->players[turn].secondsUsed == 0 );
|
XP_ASSERT( gi->players[turn].secondsUsed == 0 );
|
||||||
}
|
}
|
||||||
|
@ -1013,6 +1013,7 @@ showPrevScore( ServerCtxt* server )
|
||||||
{
|
{
|
||||||
if ( server->nv.showRobotScores ) { /* this can be changed between turns */
|
if ( server->nv.showRobotScores ) { /* this can be changed between turns */
|
||||||
XW_UtilCtxt* util = server->vol.util;
|
XW_UtilCtxt* util = server->vol.util;
|
||||||
|
XW_DUtilCtxt* dutil = server->vol.dutil;
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
const XP_UCHAR* str;
|
const XP_UCHAR* str;
|
||||||
XP_UCHAR buf[128];
|
XP_UCHAR buf[128];
|
||||||
|
@ -1025,9 +1026,9 @@ showPrevScore( ServerCtxt* server )
|
||||||
lp = &gi->players[prevTurn];
|
lp = &gi->players[prevTurn];
|
||||||
|
|
||||||
if ( LP_IS_LOCAL(lp) ) {
|
if ( LP_IS_LOCAL(lp) ) {
|
||||||
str = util_getUserString( util, STR_ROBOT_MOVED );
|
str = dutil_getUserString( dutil, STR_ROBOT_MOVED );
|
||||||
} else {
|
} else {
|
||||||
str = util_getUserString( util, STRS_REMOTE_MOVED );
|
str = dutil_getUserString( dutil, STRS_REMOTE_MOVED );
|
||||||
}
|
}
|
||||||
XP_SNPRINTF( buf, sizeof(buf), str, lp->name );
|
XP_SNPRINTF( buf, sizeof(buf), str, lp->name );
|
||||||
str = buf;
|
str = buf;
|
||||||
|
@ -2243,7 +2244,7 @@ makeTradeReportIf( ServerCtxt* server, const TrayTileSet* tradedTiles )
|
||||||
if ( server->nv.showRobotScores ) {
|
if ( server->nv.showRobotScores ) {
|
||||||
XP_UCHAR tradeBuf[64];
|
XP_UCHAR tradeBuf[64];
|
||||||
const XP_UCHAR* tradeStr =
|
const XP_UCHAR* tradeStr =
|
||||||
util_getUserQuantityString( server->vol.util, STRD_ROBOT_TRADED,
|
dutil_getUserQuantityString( server->vol.dutil, STRD_ROBOT_TRADED,
|
||||||
tradedTiles->nTiles );
|
tradedTiles->nTiles );
|
||||||
XP_SNPRINTF( tradeBuf, sizeof(tradeBuf), tradeStr,
|
XP_SNPRINTF( tradeBuf, sizeof(tradeBuf), tradeStr,
|
||||||
tradedTiles->nTiles );
|
tradedTiles->nTiles );
|
||||||
|
@ -2504,6 +2505,9 @@ server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
|
||||||
nextTurn( server, PICK_NEXT );
|
nextTurn( server, PICK_NEXT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XP_LOGF( "%s(): player %d now has %d tiles", __func__, turn,
|
||||||
|
model_getNumTilesInTray( model, turn ) );
|
||||||
|
|
||||||
return XP_TRUE;
|
return XP_TRUE;
|
||||||
} /* server_commitMove */
|
} /* server_commitMove */
|
||||||
|
|
||||||
|
@ -2706,7 +2710,7 @@ setTurn( ServerCtxt* server, XP_S16 turn )
|
||||||
|| (!amServer(server) || (0 == server->nv.pendingRegistrations)));
|
|| (!amServer(server) || (0 == server->nv.pendingRegistrations)));
|
||||||
if ( server->nv.currentTurn != turn || 1 == server->vol.gi->nPlayers ) {
|
if ( server->nv.currentTurn != turn || 1 == server->vol.gi->nPlayers ) {
|
||||||
server->nv.currentTurn = turn;
|
server->nv.currentTurn = turn;
|
||||||
server->nv.lastMoveTime = util_getCurSeconds( server->vol.util );
|
server->nv.lastMoveTime = dutil_getCurSeconds( server->vol.dutil );
|
||||||
callTurnChangeListener( server );
|
callTurnChangeListener( server );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3025,7 +3029,7 @@ server_formatDictCounts( ServerCtxt* server, XWStreamCtxt* stream,
|
||||||
Tile tile;
|
Tile tile;
|
||||||
XP_U16 nChars, nPrinted;
|
XP_U16 nChars, nPrinted;
|
||||||
XP_UCHAR buf[48];
|
XP_UCHAR buf[48];
|
||||||
const XP_UCHAR* fmt = util_getUserString( server->vol.util,
|
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
|
||||||
STRS_VALUES_HEADER );
|
STRS_VALUES_HEADER );
|
||||||
const XP_UCHAR* langName;
|
const XP_UCHAR* langName;
|
||||||
|
|
||||||
|
@ -3096,7 +3100,7 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
|
||||||
|
|
||||||
XP_ASSERT( !!server->vol.model );
|
XP_ASSERT( !!server->vol.model );
|
||||||
|
|
||||||
const XP_UCHAR* fmt = util_getUserQuantityString( server->vol.util,
|
const XP_UCHAR* fmt = dutil_getUserQuantityString( server->vol.dutil,
|
||||||
STRD_REMAINS_HEADER,
|
STRD_REMAINS_HEADER,
|
||||||
nLeft );
|
nLeft );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
||||||
|
@ -3136,7 +3140,7 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
|
||||||
XP_ASSERT( offset < sizeof(cntsBuf) );
|
XP_ASSERT( offset < sizeof(cntsBuf) );
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt = util_getUserQuantityString( server->vol.util, STRD_REMAINS_EXPL,
|
fmt = dutil_getUserQuantityString( server->vol.dutil, STRD_REMAINS_EXPL,
|
||||||
nLeft );
|
nLeft );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
||||||
stream_catString( stream, buf );
|
stream_catString( stream, buf );
|
||||||
|
@ -3226,9 +3230,9 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
XP_S16 quitter = server->nv.quitter;
|
XP_S16 quitter = server->nv.quitter;
|
||||||
XP_Bool quitterDone = XP_FALSE;
|
XP_Bool quitterDone = XP_FALSE;
|
||||||
ModelCtxt* model = server->vol.model;
|
ModelCtxt* model = server->vol.model;
|
||||||
const XP_UCHAR* addString = util_getUserString( server->vol.util,
|
const XP_UCHAR* addString = dutil_getUserString( server->vol.dutil,
|
||||||
STRD_REMAINING_TILES_ADD );
|
STRD_REMAINING_TILES_ADD );
|
||||||
const XP_UCHAR* subString = util_getUserString( server->vol.util,
|
const XP_UCHAR* subString = dutil_getUserString( server->vol.dutil,
|
||||||
STRD_UNUSED_TILES_SUB );
|
STRD_UNUSED_TILES_SUB );
|
||||||
XP_UCHAR* timeStr;
|
XP_UCHAR* timeStr;
|
||||||
CurGameInfo* gi = server->vol.gi;
|
CurGameInfo* gi = server->vol.gi;
|
||||||
|
@ -3279,8 +3283,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
XP_U16 penalty = player_timePenalty( gi, thisIndex );
|
XP_U16 penalty = player_timePenalty( gi, thisIndex );
|
||||||
if ( penalty > 0 ) {
|
if ( penalty > 0 ) {
|
||||||
XP_SNPRINTF( timeBuf, sizeof(timeBuf),
|
XP_SNPRINTF( timeBuf, sizeof(timeBuf),
|
||||||
util_getUserString(
|
dutil_getUserString( server->vol.dutil,
|
||||||
server->vol.util,
|
|
||||||
STRD_TIME_PENALTY_SUB ),
|
STRD_TIME_PENALTY_SUB ),
|
||||||
penalty ); /* positive for formatting */
|
penalty ); /* positive for formatting */
|
||||||
timeStr = timeBuf;
|
timeStr = timeBuf;
|
||||||
|
@ -3296,12 +3299,12 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
|
|
||||||
const XP_UCHAR* name = emptyStringIfNull(gi->players[thisIndex].name);
|
const XP_UCHAR* name = emptyStringIfNull(gi->players[thisIndex].name);
|
||||||
if ( 0 == placeKey ) {
|
if ( 0 == placeKey ) {
|
||||||
const XP_UCHAR* fmt = util_getUserString( server->vol.util,
|
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
|
||||||
STRDSD_PLACER );
|
STRDSD_PLACER );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), fmt, place,
|
XP_SNPRINTF( buf, sizeof(buf), fmt, place,
|
||||||
name, scores.arr[thisIndex] );
|
name, scores.arr[thisIndex] );
|
||||||
} else {
|
} else {
|
||||||
const XP_UCHAR* fmt = util_getUserString( server->vol.util,
|
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
|
||||||
placeKey );
|
placeKey );
|
||||||
XP_SNPRINTF( buf, sizeof(buf), fmt, name,
|
XP_SNPRINTF( buf, sizeof(buf), fmt, name,
|
||||||
scores.arr[thisIndex] );
|
scores.arr[thisIndex] );
|
||||||
|
|
|
@ -30,20 +30,6 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
|
||||||
PHONIES_IGNORE,
|
|
||||||
PHONIES_WARN,
|
|
||||||
PHONIES_DISALLOW
|
|
||||||
};
|
|
||||||
typedef XP_U8 XWPhoniesChoice;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
SERVER_STANDALONE,
|
|
||||||
SERVER_ISSERVER,
|
|
||||||
SERVER_ISCLIENT
|
|
||||||
};
|
|
||||||
typedef XP_U8 DeviceRole;
|
|
||||||
|
|
||||||
/* typedef struct ServerCtxt ServerCtxt; */
|
/* typedef struct ServerCtxt ServerCtxt; */
|
||||||
|
|
||||||
/* typedef struct ServerVtable { */
|
/* typedef struct ServerVtable { */
|
||||||
|
|
870
xwords4/common/smsproto.c
Normal file
870
xwords4/common/smsproto.c
Normal file
|
@ -0,0 +1,870 @@
|
||||||
|
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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 <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "smsproto.h"
|
||||||
|
#include "comtypes.h"
|
||||||
|
#include "strutils.h"
|
||||||
|
|
||||||
|
# define MAX_WAIT 3
|
||||||
|
// # define MAX_MSG_LEN 50 /* for testing */
|
||||||
|
# define MAX_LEN_BINARY 115
|
||||||
|
/* PENDING: Might want to make SEND_NOW_SIZE smaller; might as well send now
|
||||||
|
if even the smallest new message is likely to put us over. */
|
||||||
|
# define SEND_NOW_SIZE MAX_LEN_BINARY
|
||||||
|
# define SMS_PROTO_VERSION 1
|
||||||
|
# define SMS_PROTO_VERSION_COMBO 2
|
||||||
|
|
||||||
|
# define PARTIALS_FORMAT 0
|
||||||
|
|
||||||
|
typedef struct _MsgRec {
|
||||||
|
XP_U32 createSeconds;
|
||||||
|
SMSMsg msg;
|
||||||
|
} MsgRec;
|
||||||
|
|
||||||
|
typedef struct _ToPhoneRec {
|
||||||
|
XP_UCHAR phone[32];
|
||||||
|
XP_U32 createSeconds;
|
||||||
|
XP_U16 nMsgs;
|
||||||
|
XP_U16 totalSize;
|
||||||
|
MsgRec** msgs;
|
||||||
|
} ToPhoneRec;
|
||||||
|
|
||||||
|
typedef struct _MsgIDRec {
|
||||||
|
int msgID;
|
||||||
|
int count;
|
||||||
|
struct {
|
||||||
|
XP_U16 len;
|
||||||
|
XP_U8* data;
|
||||||
|
}* parts;
|
||||||
|
} MsgIDRec;
|
||||||
|
|
||||||
|
typedef struct _FromPhoneRec {
|
||||||
|
XP_UCHAR phone[32];
|
||||||
|
int nMsgIDs;
|
||||||
|
MsgIDRec* msgIDRecs;
|
||||||
|
} FromPhoneRec;
|
||||||
|
|
||||||
|
struct SMSProto {
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
|
pthread_t creator;
|
||||||
|
XP_U16 nNextID;
|
||||||
|
int lastStoredSize;
|
||||||
|
XP_U16 nToPhones;
|
||||||
|
ToPhoneRec* toPhoneRecs;
|
||||||
|
|
||||||
|
int nFromPhones;
|
||||||
|
FromPhoneRec* fromPhoneRecs;
|
||||||
|
|
||||||
|
MPSLOT;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KEY_PARTIALS PERSIST_KEY("partials")
|
||||||
|
#define KEY_NEXTID PERSIST_KEY("nextID")
|
||||||
|
|
||||||
|
static int nextMsgID( SMSProto* state );
|
||||||
|
static SMSMsgArray* toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld );
|
||||||
|
static ToPhoneRec* getForPhone( SMSProto* state, const XP_UCHAR* phone,
|
||||||
|
XP_Bool create );
|
||||||
|
static void addToRec( SMSProto* state, ToPhoneRec* rec, const XP_U8* buf,
|
||||||
|
XP_U16 buflen, XP_U32 nowSeconds );
|
||||||
|
static void addMessage( SMSProto* state, const XP_UCHAR* fromPhone, int msgID,
|
||||||
|
int indx, int count, const XP_U8* data, XP_U16 len );
|
||||||
|
static SMSMsgArray* completeMsgs( SMSProto* state, SMSMsgArray* arr,
|
||||||
|
const XP_UCHAR* fromPhone, int msgID );
|
||||||
|
static void savePartials( SMSProto* state );
|
||||||
|
static void restorePartials( SMSProto* state );
|
||||||
|
static void rmFromPhoneRec( SMSProto* state, int fromPhoneIndex );
|
||||||
|
static void freeMsgIDRec( SMSProto* state, MsgIDRec* rec, int fromPhoneIndex,
|
||||||
|
int msgIDIndex );
|
||||||
|
static void freeForPhone( SMSProto* state, const XP_UCHAR* phone );
|
||||||
|
static void freeMsg( SMSProto* state, MsgRec** msg );
|
||||||
|
static void freeRec( SMSProto* state, ToPhoneRec* rec );
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void checkThread( SMSProto* state );
|
||||||
|
#else
|
||||||
|
# define checkThread(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SMSProto*
|
||||||
|
smsproto_init( MPFORMAL XW_DUtilCtxt* dutil )
|
||||||
|
{
|
||||||
|
SMSProto* state = (SMSProto*)XP_CALLOC( mpool, sizeof(*state) );
|
||||||
|
state->dutil = dutil;
|
||||||
|
// checkThread( state ); <-- Android's calling this on background thread now
|
||||||
|
MPASSIGN( state->mpool, mpool );
|
||||||
|
|
||||||
|
XP_U16 siz = sizeof(state->nNextID);
|
||||||
|
dutil_loadPtr( state->dutil, KEY_NEXTID, &state->nNextID, &siz );
|
||||||
|
XP_LOGF( "%s(): loaded nextMsgID: %d", __func__, state->nNextID );
|
||||||
|
|
||||||
|
restorePartials( state );
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smsproto_free( SMSProto* state )
|
||||||
|
{
|
||||||
|
if ( NULL != state ) {
|
||||||
|
// checkThread( state ); <-- risky (see above)
|
||||||
|
XP_ASSERT( state->creator == 0 || state->creator == pthread_self() );
|
||||||
|
|
||||||
|
for ( XP_U16 ii = 0; ii < state->nToPhones; ++ii ) {
|
||||||
|
freeRec( state, &state->toPhoneRecs[ii] );
|
||||||
|
}
|
||||||
|
XP_FREEP( state->mpool, &state->toPhoneRecs );
|
||||||
|
|
||||||
|
if ( 0 < state->nFromPhones ) {
|
||||||
|
XP_LOGF( "%s(): freeing undelivered partial messages", __func__ );
|
||||||
|
}
|
||||||
|
while (0 < state->nFromPhones) {
|
||||||
|
FromPhoneRec* ffr = &state->fromPhoneRecs[0];
|
||||||
|
while ( 0 < ffr->nMsgIDs ) {
|
||||||
|
freeMsgIDRec( state, &ffr->msgIDRecs[0], 0, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XP_ASSERT( !state->fromPhoneRecs ); /* above nulls this once empty */
|
||||||
|
|
||||||
|
XP_FREEP( state->mpool, &state );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Maintain a list of pending messages per phone number. When called and it's
|
||||||
|
* been at least some amount of time since we last added something, or at
|
||||||
|
* least some longer time since the oldest message was added, return an array
|
||||||
|
* of messages ready to send via the device's raw SMS (i.e. respecting its
|
||||||
|
* size limits.)
|
||||||
|
|
||||||
|
* Pass in the current time, as that's easier than keeping an instance of
|
||||||
|
* UtilCtxt around.
|
||||||
|
*/
|
||||||
|
SMSMsgArray*
|
||||||
|
smsproto_prepOutbound( SMSProto* state, const XP_U8* buf,
|
||||||
|
XP_U16 buflen, const XP_UCHAR* toPhone,
|
||||||
|
XP_Bool forceOld, XP_U16* waitSecsP )
|
||||||
|
{
|
||||||
|
SMSMsgArray* result = NULL;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
XP_UCHAR* checksum = dutil_md5sum( state->dutil, buf, buflen );
|
||||||
|
XP_LOGF( "%s(): len=%d, sum=%s, toPhone=%s", __func__, buflen,
|
||||||
|
checksum, toPhone );
|
||||||
|
XP_FREEP( state->mpool, &checksum );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
checkThread( state );
|
||||||
|
ToPhoneRec* rec = getForPhone( state, toPhone, !!buf );
|
||||||
|
|
||||||
|
/* First, add the new message (if present) to the array */
|
||||||
|
XP_U32 nowSeconds = dutil_getCurSeconds( state->dutil );
|
||||||
|
if ( !!buf ) {
|
||||||
|
addToRec( state, rec, buf, buflen, nowSeconds );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rec will be non-null if there's something in it */
|
||||||
|
XP_Bool doSend = XP_FALSE;
|
||||||
|
if ( rec != NULL ) {
|
||||||
|
doSend = forceOld
|
||||||
|
|| rec->totalSize > SEND_NOW_SIZE
|
||||||
|
|| MAX_WAIT <= nowSeconds - rec->createSeconds;
|
||||||
|
/* other criteria? */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( doSend ) {
|
||||||
|
result = toMsgs( state, rec, forceOld );
|
||||||
|
freeForPhone( state, toPhone );
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_U16 waitSecs = 0;
|
||||||
|
if ( !result && !!rec && (rec->nMsgs > 0) ) {
|
||||||
|
waitSecs = MAX_WAIT - (nowSeconds - rec->createSeconds);
|
||||||
|
}
|
||||||
|
*waitSecsP = waitSecs;
|
||||||
|
|
||||||
|
XP_LOGF( "%s() => %p (len=%d, *waitSecs=%d)", __func__, result,
|
||||||
|
!!result ? result->nMsgs : 0, *waitSecsP );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SMSMsgArray*
|
||||||
|
appendMsg( SMSProto* state, SMSMsgArray* arr, SMSMsg* msg )
|
||||||
|
{
|
||||||
|
if ( NULL == arr ) {
|
||||||
|
arr = XP_CALLOC( state->mpool, sizeof(*arr) );
|
||||||
|
}
|
||||||
|
|
||||||
|
arr->msgs = XP_REALLOC( state->mpool, arr->msgs,
|
||||||
|
(arr->nMsgs + 1) * sizeof(*arr->msgs) );
|
||||||
|
arr->msgs[arr->nMsgs++] = *msg;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SMSMsgArray*
|
||||||
|
smsproto_prepInbound( SMSProto* state, const XP_UCHAR* fromPhone,
|
||||||
|
const XP_U8* data, XP_U16 len )
|
||||||
|
{
|
||||||
|
XP_LOGF( "%s(): len=%d, fromPhone=%s", __func__, len, fromPhone );
|
||||||
|
checkThread( state );
|
||||||
|
|
||||||
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool)
|
||||||
|
dutil_getVTManager(state->dutil) );
|
||||||
|
stream_putBytes( stream, data, len );
|
||||||
|
|
||||||
|
SMSMsgArray* result = NULL;
|
||||||
|
XP_U8 proto;
|
||||||
|
if ( stream_gotU8( stream, &proto ) ) {
|
||||||
|
switch ( proto ) {
|
||||||
|
case SMS_PROTO_VERSION: {
|
||||||
|
XP_U8 msgID, indx, count;
|
||||||
|
if ( stream_gotU8( stream, &msgID )
|
||||||
|
&& stream_gotU8( stream, &indx )
|
||||||
|
&& stream_gotU8( stream, &count )
|
||||||
|
&& indx < count ) {
|
||||||
|
XP_U16 len = stream_getSize( stream );
|
||||||
|
XP_U8 buf[len];
|
||||||
|
stream_getBytes( stream, buf, len );
|
||||||
|
addMessage( state, fromPhone, msgID, indx, count, buf, len );
|
||||||
|
result = completeMsgs( state, result, fromPhone, msgID );
|
||||||
|
savePartials( state );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SMS_PROTO_VERSION_COMBO: {
|
||||||
|
XP_U8 oneLen, msgID;
|
||||||
|
while ( stream_gotU8( stream, &oneLen )
|
||||||
|
&& stream_gotU8( stream, &msgID ) ) {
|
||||||
|
XP_U8 buf[oneLen];
|
||||||
|
if ( stream_gotBytes( stream, buf, oneLen ) ) {
|
||||||
|
SMSMsg msg = { .len = oneLen,
|
||||||
|
.msgID = msgID,
|
||||||
|
.data = XP_MALLOC( state->mpool, oneLen ),
|
||||||
|
};
|
||||||
|
XP_MEMCPY( msg.data, buf, oneLen );
|
||||||
|
result = appendMsg( state, result, &msg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
XP_LOGF( "%s(): unexpected proto %d", __func__, proto );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_destroy( stream );
|
||||||
|
|
||||||
|
XP_LOGF( "%s() => %p (len=%d)", __func__, result, (!!result) ? result->nMsgs : 0 );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr )
|
||||||
|
{
|
||||||
|
checkThread( state );
|
||||||
|
|
||||||
|
for ( int ii = 0; ii < arr->nMsgs; ++ii ) {
|
||||||
|
XP_FREEP( state->mpool, &arr->msgs[ii].data );
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_FREEP( state->mpool, &arr->msgs );
|
||||||
|
XP_FREEP( state->mpool, &arr );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freeMsg( SMSProto* state, MsgRec** msgp )
|
||||||
|
{
|
||||||
|
XP_FREEP( state->mpool, &(*msgp)->msg.data );
|
||||||
|
XP_FREEP( state->mpool, msgp );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freeRec( SMSProto* state, ToPhoneRec* rec )
|
||||||
|
{
|
||||||
|
for ( XP_U16 jj = 0; jj < rec->nMsgs; ++jj ) {
|
||||||
|
freeMsg( state, &rec->msgs[jj] );
|
||||||
|
}
|
||||||
|
XP_FREEP( state->mpool, &rec->msgs );
|
||||||
|
}
|
||||||
|
|
||||||
|
static ToPhoneRec*
|
||||||
|
getForPhone( SMSProto* state, const XP_UCHAR* phone, XP_Bool create )
|
||||||
|
{
|
||||||
|
ToPhoneRec* rec = NULL;
|
||||||
|
for ( XP_U16 ii = 0; !rec && ii < state->nToPhones; ++ii ) {
|
||||||
|
if ( 0 == XP_STRCMP( state->toPhoneRecs[ii].phone, phone ) ) {
|
||||||
|
rec = &state->toPhoneRecs[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !rec && create ) {
|
||||||
|
state->toPhoneRecs = XP_REALLOC( state->mpool, state->toPhoneRecs,
|
||||||
|
(1 + state->nToPhones) * sizeof(*state->toPhoneRecs) );
|
||||||
|
rec = &state->toPhoneRecs[state->nToPhones++];
|
||||||
|
XP_MEMSET( rec, 0, sizeof(*rec) );
|
||||||
|
XP_STRCAT( rec->phone, phone );
|
||||||
|
}
|
||||||
|
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freeForPhone( SMSProto* state, const XP_UCHAR* phone )
|
||||||
|
{
|
||||||
|
for ( XP_U16 ii = 0; ii < state->nToPhones; ++ii ) {
|
||||||
|
if ( 0 == XP_STRCMP( state->toPhoneRecs[ii].phone, phone ) ) {
|
||||||
|
freeRec( state, &state->toPhoneRecs[ii] );
|
||||||
|
|
||||||
|
XP_U16 nAbove = state->nToPhones - ii - 1;
|
||||||
|
XP_ASSERT( nAbove >= 0 );
|
||||||
|
if ( nAbove > 0 ) {
|
||||||
|
XP_MEMMOVE( &state->toPhoneRecs[ii], &state->toPhoneRecs[ii+1],
|
||||||
|
nAbove * sizeof(*state->toPhoneRecs) );
|
||||||
|
}
|
||||||
|
--state->nToPhones;
|
||||||
|
if ( 0 == state->nToPhones ) {
|
||||||
|
XP_FREEP( state->mpool, &state->toPhoneRecs );
|
||||||
|
} else {
|
||||||
|
state->toPhoneRecs = XP_REALLOC( state->mpool, state->toPhoneRecs,
|
||||||
|
state->nToPhones * sizeof(*state->toPhoneRecs) );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
addToRec( SMSProto* state, ToPhoneRec* rec, const XP_U8* buf, XP_U16 buflen,
|
||||||
|
XP_U32 nowSeconds )
|
||||||
|
{
|
||||||
|
MsgRec* mRec = XP_CALLOC( state->mpool, sizeof(*rec) );
|
||||||
|
mRec->msg.len = buflen;
|
||||||
|
mRec->msg.data = XP_MALLOC( state->mpool, buflen );
|
||||||
|
XP_MEMCPY( mRec->msg.data, buf, buflen );
|
||||||
|
mRec->createSeconds = nowSeconds;
|
||||||
|
|
||||||
|
rec->msgs = XP_REALLOC( state->mpool, rec->msgs, (1 + rec->nMsgs) * sizeof(*rec->msgs) );
|
||||||
|
rec->msgs[rec->nMsgs++] = mRec;
|
||||||
|
rec->totalSize += buflen;
|
||||||
|
XP_LOGF( "%s(): added msg to %s of len %d; total now %d", __func__, rec->phone,
|
||||||
|
buflen, rec->totalSize );
|
||||||
|
|
||||||
|
if ( rec->nMsgs == 1 ) {
|
||||||
|
rec->createSeconds = nowSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MsgIDRec*
|
||||||
|
getMsgIDRec( SMSProto* state, const XP_UCHAR* fromPhone, int msgID,
|
||||||
|
XP_Bool addMissing, int* fromPhoneIndex, int* msgIDIndex )
|
||||||
|
{
|
||||||
|
MsgIDRec* result = NULL;
|
||||||
|
|
||||||
|
FromPhoneRec* fromPhoneRec = NULL;
|
||||||
|
for ( int ii = 0; ii < state->nFromPhones; ++ii ) {
|
||||||
|
if ( 0 == XP_STRCMP( state->fromPhoneRecs[ii].phone, fromPhone ) ) {
|
||||||
|
fromPhoneRec = &state->fromPhoneRecs[ii];
|
||||||
|
*fromPhoneIndex = ii;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and add if not found
|
||||||
|
if ( NULL == fromPhoneRec && addMissing ) {
|
||||||
|
state->fromPhoneRecs =
|
||||||
|
XP_REALLOC( state->mpool, state->fromPhoneRecs,
|
||||||
|
(state->nFromPhones + 1) * sizeof(*state->fromPhoneRecs) );
|
||||||
|
*fromPhoneIndex = state->nFromPhones;
|
||||||
|
fromPhoneRec = &state->fromPhoneRecs[state->nFromPhones++];
|
||||||
|
XP_MEMSET( fromPhoneRec, 0, sizeof(*fromPhoneRec) );
|
||||||
|
XP_STRCAT( fromPhoneRec->phone, fromPhone );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now find msgID record
|
||||||
|
if ( NULL != fromPhoneRec ) {
|
||||||
|
for ( int ii = 0; ii < fromPhoneRec->nMsgIDs; ++ii ) {
|
||||||
|
if ( fromPhoneRec->msgIDRecs[ii].msgID == msgID ) {
|
||||||
|
result = &fromPhoneRec->msgIDRecs[ii];
|
||||||
|
*msgIDIndex = ii;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and add if not found
|
||||||
|
if ( NULL == result && addMissing ) {
|
||||||
|
fromPhoneRec->msgIDRecs = XP_REALLOC( state->mpool, fromPhoneRec->msgIDRecs,
|
||||||
|
(fromPhoneRec->nMsgIDs + 1)
|
||||||
|
* sizeof(*fromPhoneRec->msgIDRecs) );
|
||||||
|
MsgIDRec newRec = { .msgID = msgID };
|
||||||
|
*msgIDIndex = fromPhoneRec->nMsgIDs;
|
||||||
|
result = &fromPhoneRec->msgIDRecs[fromPhoneRec->nMsgIDs];
|
||||||
|
fromPhoneRec->msgIDRecs[fromPhoneRec->nMsgIDs++] = newRec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages that are split gather here until complete
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
addMessage( SMSProto* state, const XP_UCHAR* fromPhone, int msgID, int indx,
|
||||||
|
int count, const XP_U8* data, XP_U16 len )
|
||||||
|
{
|
||||||
|
XP_LOGF( "phone=%s, msgID=%d, %d/%d", fromPhone, msgID, indx, count );
|
||||||
|
XP_ASSERT( 0 < len );
|
||||||
|
MsgIDRec* msgIDRec;
|
||||||
|
for ( ; ; ) {
|
||||||
|
int fromPhoneIndex;
|
||||||
|
int msgIDIndex;
|
||||||
|
msgIDRec = getMsgIDRec( state, fromPhone, msgID, XP_TRUE,
|
||||||
|
&fromPhoneIndex, &msgIDIndex );
|
||||||
|
|
||||||
|
/* sanity check... */
|
||||||
|
if ( msgIDRec->count == 0 || msgIDRec->count == count ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freeMsgIDRec( state, msgIDRec, fromPhoneIndex, msgIDIndex );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if it's new, fill in missing fields */
|
||||||
|
if ( msgIDRec->count == 0 ) {
|
||||||
|
msgIDRec->count = count; /* in case it's new */
|
||||||
|
msgIDRec->parts = XP_CALLOC( state->mpool, count * sizeof(*msgIDRec->parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_ASSERT( msgIDRec->parts[indx].len == 0
|
||||||
|
|| msgIDRec->parts[indx].len == len ); /* replace with same ok */
|
||||||
|
msgIDRec->parts[indx].len = len;
|
||||||
|
XP_FREEP( state->mpool, &msgIDRec->parts[indx].data ); /* in case non-null (replacement) */
|
||||||
|
msgIDRec->parts[indx].data = XP_MALLOC( state->mpool, len );
|
||||||
|
XP_MEMCPY( msgIDRec->parts[indx].data, data, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rmFromPhoneRec( SMSProto* state, int fromPhoneIndex )
|
||||||
|
{
|
||||||
|
FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex];
|
||||||
|
XP_ASSERT( fromPhoneRec->nMsgIDs == 0 );
|
||||||
|
XP_FREEP( state->mpool, &fromPhoneRec->msgIDRecs );
|
||||||
|
|
||||||
|
if ( --state->nFromPhones == 0 ) {
|
||||||
|
XP_FREEP( state->mpool, &state->fromPhoneRecs );
|
||||||
|
} else {
|
||||||
|
XP_U16 nAbove = state->nFromPhones - fromPhoneIndex;
|
||||||
|
XP_ASSERT( nAbove >= 0 );
|
||||||
|
if ( nAbove > 0 ) {
|
||||||
|
XP_MEMMOVE( &state->fromPhoneRecs[fromPhoneIndex], &state->fromPhoneRecs[fromPhoneIndex+1],
|
||||||
|
nAbove * sizeof(*state->fromPhoneRecs) );
|
||||||
|
}
|
||||||
|
state->fromPhoneRecs = XP_REALLOC( state->mpool, state->fromPhoneRecs,
|
||||||
|
state->nFromPhones * sizeof(*state->fromPhoneRecs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freeMsgIDRec( SMSProto* state, MsgIDRec* rec, int fromPhoneIndex, int msgIDIndex )
|
||||||
|
{
|
||||||
|
FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex];
|
||||||
|
MsgIDRec* msgIDRec = &fromPhoneRec->msgIDRecs[msgIDIndex];
|
||||||
|
XP_ASSERT( msgIDRec == rec );
|
||||||
|
|
||||||
|
for ( int ii = 0; ii < msgIDRec->count; ++ii ) {
|
||||||
|
XP_FREEP( state->mpool, &msgIDRec->parts[ii].data );
|
||||||
|
}
|
||||||
|
XP_FREEP( state->mpool, &msgIDRec->parts );
|
||||||
|
|
||||||
|
if ( --fromPhoneRec->nMsgIDs > 0 ) {
|
||||||
|
XP_U16 nAbove = fromPhoneRec->nMsgIDs - msgIDIndex;
|
||||||
|
XP_ASSERT( nAbove >= 0 );
|
||||||
|
if ( nAbove > 0 ) {
|
||||||
|
XP_MEMMOVE( &fromPhoneRec->msgIDRecs[msgIDIndex], &fromPhoneRec->msgIDRecs[msgIDIndex+1],
|
||||||
|
nAbove * sizeof(*fromPhoneRec->msgIDRecs) );
|
||||||
|
}
|
||||||
|
fromPhoneRec->msgIDRecs = XP_REALLOC( state->mpool, fromPhoneRec->msgIDRecs,
|
||||||
|
fromPhoneRec->nMsgIDs
|
||||||
|
* sizeof(*fromPhoneRec->msgIDRecs));
|
||||||
|
} else {
|
||||||
|
rmFromPhoneRec( state, fromPhoneIndex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
savePartials( SMSProto* state )
|
||||||
|
{
|
||||||
|
checkThread( state );
|
||||||
|
|
||||||
|
XWStreamCtxt* stream
|
||||||
|
= mem_stream_make_raw( MPPARM(state->mpool)
|
||||||
|
dutil_getVTManager(state->dutil) );
|
||||||
|
stream_putU8( stream, PARTIALS_FORMAT );
|
||||||
|
|
||||||
|
stream_putU8( stream, state->nFromPhones );
|
||||||
|
for ( int ii = 0; ii < state->nFromPhones; ++ii ) {
|
||||||
|
const FromPhoneRec* rec = &state->fromPhoneRecs[ii];
|
||||||
|
stringToStream( stream, rec->phone );
|
||||||
|
stream_putU8( stream, rec->nMsgIDs );
|
||||||
|
for ( int jj = 0; jj < rec->nMsgIDs; ++jj ) {
|
||||||
|
MsgIDRec* mir = &rec->msgIDRecs[jj];
|
||||||
|
stream_putU16( stream, mir->msgID );
|
||||||
|
stream_putU8( stream, mir->count );
|
||||||
|
|
||||||
|
/* There's an array here. It may be sparse. Save a len of 0 */
|
||||||
|
for ( int kk = 0; kk < mir->count; ++kk ) {
|
||||||
|
int len = mir->parts[kk].len;
|
||||||
|
stream_putU8( stream, len );
|
||||||
|
stream_putBytes( stream, mir->parts[kk].data, len );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_U16 newSize = stream_getSize( stream );
|
||||||
|
if ( state->lastStoredSize == 2 && newSize == 2 ) {
|
||||||
|
XP_LOGF( "%s(): not storing empty again", __func__ );
|
||||||
|
} else {
|
||||||
|
dutil_storeStream( state->dutil, KEY_PARTIALS, stream );
|
||||||
|
state->lastStoredSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_destroy( stream );
|
||||||
|
|
||||||
|
LOG_RETURN_VOID();
|
||||||
|
} /* savePartials */
|
||||||
|
|
||||||
|
static void
|
||||||
|
restorePartials( SMSProto* state )
|
||||||
|
{
|
||||||
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool)
|
||||||
|
dutil_getVTManager(state->dutil) );
|
||||||
|
dutil_loadStream( state->dutil, KEY_PARTIALS, stream );
|
||||||
|
if ( stream_getSize( stream ) >= 1
|
||||||
|
&& PARTIALS_FORMAT == stream_getU8( stream ) ) {
|
||||||
|
int nFromPhones = stream_getU8( stream );
|
||||||
|
for ( int ii = 0; ii < nFromPhones; ++ii ) {
|
||||||
|
XP_UCHAR phone[32];
|
||||||
|
(void)stringFromStreamHere( stream, phone, VSIZE(phone) );
|
||||||
|
int nMsgIDs = stream_getU8( stream );
|
||||||
|
XP_LOGF( "%s(): got %d message records for phone %s", __func__,
|
||||||
|
nMsgIDs, phone );
|
||||||
|
for ( int jj = 0; jj < nMsgIDs; ++jj ) {
|
||||||
|
XP_U16 msgID = stream_getU16( stream );
|
||||||
|
int count = stream_getU8( stream );
|
||||||
|
XP_LOGF( "%s(): got %d records for msgID %d", __func__, count, msgID );
|
||||||
|
for ( int kk = 0; kk < count; ++kk ) {
|
||||||
|
int len = stream_getU8( stream );
|
||||||
|
if ( 0 < len ) {
|
||||||
|
XP_U8 buf[len];
|
||||||
|
stream_getBytes( stream, buf, len );
|
||||||
|
addMessage( state, phone, msgID, kk, count, buf, len );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream_destroy( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
static SMSMsgArray*
|
||||||
|
completeMsgs( SMSProto* state, SMSMsgArray* arr, const XP_UCHAR* fromPhone,
|
||||||
|
int msgID )
|
||||||
|
{
|
||||||
|
int fromPhoneIndex, msgIDIndex;
|
||||||
|
MsgIDRec* rec = getMsgIDRec( state, fromPhone, msgID, XP_FALSE,
|
||||||
|
&fromPhoneIndex, &msgIDIndex);
|
||||||
|
if ( !rec ) {
|
||||||
|
XP_LOGF( "%s(): no rec for phone %s, msgID %d", __func__, fromPhone, msgID );
|
||||||
|
XP_ASSERT( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
XP_Bool haveAll = XP_TRUE;
|
||||||
|
for ( int ii = 0; ii < rec->count; ++ii ) {
|
||||||
|
if ( rec->parts[ii].len == 0 ) {
|
||||||
|
haveAll = XP_FALSE;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
len += rec->parts[ii].len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( haveAll ) {
|
||||||
|
SMSMsg msg = { .len = len,
|
||||||
|
.msgID = msgID,
|
||||||
|
.data = XP_MALLOC( state->mpool, len ),
|
||||||
|
};
|
||||||
|
XP_U8* ptr = msg.data;
|
||||||
|
for ( int ii = 0; ii < rec->count; ++ii ) {
|
||||||
|
XP_MEMCPY( ptr, rec->parts[ii].data, rec->parts[ii].len );
|
||||||
|
ptr += rec->parts[ii].len;
|
||||||
|
}
|
||||||
|
arr = appendMsg( state, arr, &msg );
|
||||||
|
|
||||||
|
freeMsgIDRec( state, rec, fromPhoneIndex, msgIDIndex );
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SMSMsgArray*
|
||||||
|
toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld )
|
||||||
|
{
|
||||||
|
SMSMsgArray* result = NULL;
|
||||||
|
|
||||||
|
for ( XP_U16 ii = 0; ii < rec->nMsgs; ) {
|
||||||
|
// XP_LOGF( "%s(): looking at msg %d of %d", __func__, ii, rec->nMsgs );
|
||||||
|
XP_U16 count = (rec->msgs[ii]->msg.len + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY;
|
||||||
|
|
||||||
|
/* First, see if this message and some number of its neighbors can be
|
||||||
|
combined */
|
||||||
|
int last = ii;
|
||||||
|
int sum = 0;
|
||||||
|
if ( count == 1 && !forceOld ) {
|
||||||
|
for ( ; last < rec->nMsgs; ++last ) {
|
||||||
|
int nextLen = rec->msgs[last]->msg.len;
|
||||||
|
if ( sum + nextLen > MAX_LEN_BINARY ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sum += nextLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( last > ii ) {
|
||||||
|
int nMsgs = last - ii;
|
||||||
|
if ( nMsgs > 1 ) {
|
||||||
|
XP_LOGF( "%s(): combining %d through %d (%d msgs)", __func__, ii,
|
||||||
|
last - 1, nMsgs );
|
||||||
|
}
|
||||||
|
int len = 1 + sum + (nMsgs * 2); /* 1: len & msgID */
|
||||||
|
SMSMsg newMsg = { .len = len,
|
||||||
|
.data = XP_MALLOC( state->mpool, len )
|
||||||
|
};
|
||||||
|
int indx = 0;
|
||||||
|
newMsg.data[indx++] = SMS_PROTO_VERSION_COMBO;
|
||||||
|
for ( int jj = ii; jj < last; ++jj ) {
|
||||||
|
const SMSMsg* msg = &rec->msgs[jj]->msg;
|
||||||
|
newMsg.data[indx++] = msg->len;
|
||||||
|
newMsg.data[indx++] = nextMsgID( state );
|
||||||
|
XP_MEMCPY( &newMsg.data[indx], msg->data, msg->len ); /* bad! */
|
||||||
|
indx += msg->len;
|
||||||
|
}
|
||||||
|
result = appendMsg( state, result, &newMsg );
|
||||||
|
ii = last;
|
||||||
|
} else {
|
||||||
|
int msgID = nextMsgID( state );
|
||||||
|
const SMSMsg* msg = &rec->msgs[ii]->msg;
|
||||||
|
XP_U8* nextStart = msg->data;
|
||||||
|
XP_U16 lenLeft = msg->len;
|
||||||
|
for ( XP_U16 indx = 0; indx < count; ++indx ) {
|
||||||
|
XP_ASSERT( lenLeft > 0 );
|
||||||
|
XP_U16 useLen = lenLeft;
|
||||||
|
if ( useLen >= MAX_LEN_BINARY ) {
|
||||||
|
useLen = MAX_LEN_BINARY;
|
||||||
|
}
|
||||||
|
lenLeft -= useLen;
|
||||||
|
|
||||||
|
SMSMsg newMsg = { .len = useLen + 4,
|
||||||
|
.data = XP_MALLOC( state->mpool, useLen + 4 )
|
||||||
|
};
|
||||||
|
newMsg.data[0] = SMS_PROTO_VERSION;
|
||||||
|
newMsg.data[1] = msgID;
|
||||||
|
newMsg.data[2] = indx;
|
||||||
|
newMsg.data[3] = count;
|
||||||
|
XP_MEMCPY( newMsg.data + 4, nextStart, useLen );
|
||||||
|
nextStart += useLen;
|
||||||
|
|
||||||
|
result = appendMsg( state, result, &newMsg );
|
||||||
|
}
|
||||||
|
++ii;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} /* toMsgs */
|
||||||
|
|
||||||
|
static int
|
||||||
|
nextMsgID( SMSProto* state )
|
||||||
|
{
|
||||||
|
int result = ++state->nNextID % 0x000000FF;
|
||||||
|
dutil_storePtr( state->dutil, KEY_NEXTID, &state->nNextID,
|
||||||
|
sizeof(state->nNextID) );
|
||||||
|
LOG_RETURNF( "%d", result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void
|
||||||
|
checkThread( SMSProto* state )
|
||||||
|
{
|
||||||
|
pthread_t curThread = pthread_self();
|
||||||
|
if ( 0 == state->creator ) {
|
||||||
|
state->creator = curThread;
|
||||||
|
} else {
|
||||||
|
/* If this is firing will need to use a mutex to make SMSProto access
|
||||||
|
thread-safe */
|
||||||
|
XP_ASSERT( state->creator == curThread );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil )
|
||||||
|
{
|
||||||
|
LOG_FUNC();
|
||||||
|
SMSProto* state = smsproto_init( mpool, dutil );
|
||||||
|
|
||||||
|
const int smallSiz = 20;
|
||||||
|
const char* phones[] = {"1234", "3456", "5467", "9877"};
|
||||||
|
const char* buf = "asoidfaisdfoausdf aiousdfoiu asodfu oiuasdofi oiuaosiduf oaisudf oiasd f"
|
||||||
|
";oiaisdjfljiojaklj asdlkjalskdjf laksjd flkjasdlfkj aldsjkf lsakdjf lkjsad flkjsd fl;kj"
|
||||||
|
"asdifaoaosidfoiauosidufoaus doifuoaiusdoifu aoisudfoaisd foia sdoifuasodfu aosiud foiuas odfiu asd"
|
||||||
|
"aosdoiaosdoiisidfoiosi isoidufoisu doifuoisud oiuoi98a90iu-asjdfoiasdfij"
|
||||||
|
;
|
||||||
|
const XP_Bool forceOld = XP_TRUE;
|
||||||
|
|
||||||
|
SMSMsgArray* arrs[VSIZE(phones)];
|
||||||
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
||||||
|
arrs[ii] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop until all the messages are ready. */
|
||||||
|
for ( XP_Bool firstTime = XP_TRUE; ; firstTime = XP_FALSE) {
|
||||||
|
XP_Bool allDone = XP_TRUE;
|
||||||
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
if ( firstTime ) {
|
||||||
|
XP_U16 len = (ii + 1) * 30;
|
||||||
|
arrs[ii] = smsproto_prepOutbound( state, (XP_U8*)buf, len, phones[ii],
|
||||||
|
forceOld, &waitSecs );
|
||||||
|
} else if ( NULL == arrs[ii]) {
|
||||||
|
arrs[ii] = smsproto_prepOutbound( state, NULL, 0, phones[ii],
|
||||||
|
forceOld, &waitSecs );
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allDone = allDone & (waitSecs == 0 && !!arrs[ii]);
|
||||||
|
}
|
||||||
|
if ( allDone ) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
(void)sleep( 2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( int indx = 0; ; ++indx ) {
|
||||||
|
XP_Bool haveOne = XP_FALSE;
|
||||||
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
||||||
|
if (!!arrs[ii] && indx < arrs[ii]->nMsgs) {
|
||||||
|
haveOne = XP_TRUE;
|
||||||
|
SMSMsgArray* outArr = smsproto_prepInbound( state, phones[ii],
|
||||||
|
arrs[ii]->msgs[indx].data,
|
||||||
|
arrs[ii]->msgs[indx].len );
|
||||||
|
if ( !!outArr ) {
|
||||||
|
SMSMsg* msg = &outArr->msgs[0];
|
||||||
|
XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID );
|
||||||
|
XP_ASSERT( outArr->nMsgs == 1 );
|
||||||
|
XP_ASSERT( 0 == memcmp(buf, msg->data, (ii + 1) * 30) );
|
||||||
|
smsproto_freeMsgArray( state, outArr );
|
||||||
|
|
||||||
|
smsproto_freeMsgArray( state, arrs[ii] );
|
||||||
|
arrs[ii] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!haveOne) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now let's send a bunch of small messages that should get combined */
|
||||||
|
for ( int nUsed = 0; ; ++nUsed ) {
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
SMSMsgArray* sendArr = smsproto_prepOutbound( state, (XP_U8*)&buf[nUsed],
|
||||||
|
smallSiz, phones[0],
|
||||||
|
XP_FALSE, &waitSecs );
|
||||||
|
if ( sendArr == NULL ) {
|
||||||
|
XP_LOGF( "%s(): msg[%d] of len %d sent; still not ready", __func__, nUsed, smallSiz );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_ASSERT( waitSecs == 0 );
|
||||||
|
int totalBack = 0;
|
||||||
|
for ( int jj = 0; jj < sendArr->nMsgs; ++jj ) {
|
||||||
|
SMSMsgArray* recvArr = smsproto_prepInbound( state, phones[0],
|
||||||
|
sendArr->msgs[jj].data,
|
||||||
|
sendArr->msgs[jj].len );
|
||||||
|
|
||||||
|
if ( !!recvArr ) {
|
||||||
|
XP_LOGF( "%s(): got %d msgs (from %d)", __func__, recvArr->nMsgs, nUsed + 1 );
|
||||||
|
for ( int kk = 0; kk < recvArr->nMsgs; ++kk ) {
|
||||||
|
SMSMsg* msg = &recvArr->msgs[kk];
|
||||||
|
XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID );
|
||||||
|
XP_ASSERT( msg->len == smallSiz );
|
||||||
|
XP_ASSERT( 0 == memcmp( msg->data, &buf[totalBack], smallSiz ) );
|
||||||
|
++totalBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
smsproto_freeMsgArray( state, recvArr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XP_ASSERT( forceOld || totalBack == nUsed + 1 );
|
||||||
|
XP_LOGF( "%s(): %d messages checked out", __func__, totalBack );
|
||||||
|
smsproto_freeMsgArray( state, sendArr );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now let's add a too-long message and unpack only the first part. Make
|
||||||
|
sure it's cleaned up correctly */
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
SMSMsgArray* arr = smsproto_prepOutbound( state, (XP_U8*)buf, 200, "33333", XP_TRUE, &waitSecs );
|
||||||
|
XP_ASSERT( !!arr && arr->nMsgs > 1 );
|
||||||
|
/* add only part 1 */
|
||||||
|
SMSMsgArray* out = smsproto_prepInbound( state, "33333", arr->msgs[0].data, arr->msgs[0].len );
|
||||||
|
XP_ASSERT( !out );
|
||||||
|
smsproto_freeMsgArray( state, arr );
|
||||||
|
|
||||||
|
|
||||||
|
/* now a message that's unpacked across multiple sessions to test store/load */
|
||||||
|
XP_LOGF( "%s(): testing store/restore", __func__ );
|
||||||
|
arr = smsproto_prepOutbound( state, (XP_U8*)buf, 200, "33333", XP_TRUE, &waitSecs );
|
||||||
|
for ( int ii = 0; ii < arr->nMsgs; ++ii ) {
|
||||||
|
SMSMsgArray* out = smsproto_prepInbound( state, "33333", arr->msgs[ii].data,
|
||||||
|
arr->msgs[ii].len );
|
||||||
|
if ( !!out ) {
|
||||||
|
XP_ASSERT( out->nMsgs == 1);
|
||||||
|
XP_LOGF( "%s(): got the message on the %dth loop", __func__, ii );
|
||||||
|
XP_ASSERT( out->msgs[0].len == 200 );
|
||||||
|
XP_ASSERT( 0 == memcmp( out->msgs[0].data, buf, 200 ) );
|
||||||
|
smsproto_freeMsgArray( state, out );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
smsproto_free( state ); /* give it a chance to store state */
|
||||||
|
state = smsproto_init( mpool, dutil );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Really bad to pass a different state than was created with, but now
|
||||||
|
since only mpool is used and it's the same for all states, let it
|
||||||
|
go. */
|
||||||
|
smsproto_freeMsgArray( state, arr ); /* give it a chance to store state */
|
||||||
|
|
||||||
|
smsproto_free( state );
|
||||||
|
LOG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
#endif
|
85
xwords4/common/smsproto.h
Normal file
85
xwords4/common/smsproto.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SMSPROTO_H__
|
||||||
|
#define __SMSPROTO_H__
|
||||||
|
|
||||||
|
// The sms protocol is getting more complicated as I add message combining. So
|
||||||
|
// let's try to move it into C where it can be tested in linux.
|
||||||
|
//
|
||||||
|
// How the protocol works. Clients want to send data packets to phones and
|
||||||
|
// receive from phones. They use a send(byte[] data, String phone) method, and
|
||||||
|
// provide a callback that's called when packets arrive. Internally this
|
||||||
|
// module buffers messages on a per-number basis (new post-java feature) and
|
||||||
|
// periodically, based on a timer that's set when buffered data is waiting to
|
||||||
|
// be sent, gathers messages and transmits them. Probably it'll wait a few
|
||||||
|
// seconds after the last message was enqueued, the idea being that SMS is not
|
||||||
|
// expected to be fast and that sending lots of small messages is bad.
|
||||||
|
//
|
||||||
|
// Because there's a max size to SMS messages any [combined] message that's
|
||||||
|
// too big is broken up. To keep things simple the two processes, combining
|
||||||
|
// and breaking, are independent; there's no attempt to avoid combining
|
||||||
|
// messages even when doing so creates something that will have to be broken
|
||||||
|
// up.
|
||||||
|
//
|
||||||
|
// Received messages (buffers) are recombined (if the result of breaking up)
|
||||||
|
// then split (if the result of combining), and the constituent data packets
|
||||||
|
// are returned to the app as an array of byte[].
|
||||||
|
|
||||||
|
#include "xptypes.h"
|
||||||
|
#include "mempool.h" /* debug only */
|
||||||
|
|
||||||
|
typedef struct SMSProto SMSProto;
|
||||||
|
|
||||||
|
typedef struct _SMSMsg {
|
||||||
|
XP_U16 len;
|
||||||
|
XP_U16 msgID;
|
||||||
|
XP_U8* data;
|
||||||
|
} SMSMsg;
|
||||||
|
|
||||||
|
typedef struct _SMSMsgArray {
|
||||||
|
XP_U16 nMsgs;
|
||||||
|
SMSMsg* msgs;
|
||||||
|
} SMSMsgArray;
|
||||||
|
|
||||||
|
struct SMSProto* smsproto_init( MPFORMAL XW_DUtilCtxt* dutil );
|
||||||
|
void smsproto_free( SMSProto* state );
|
||||||
|
|
||||||
|
/* Return ptr to structure if one's ready to be sent, otherwise null. Caller *
|
||||||
|
* should interpret null as meaning it's meant to call again. To support that,
|
||||||
|
* null buf is legit.
|
||||||
|
*
|
||||||
|
* When send() returns a non-null value, that value must be passed to
|
||||||
|
* freeMsgArray() or there will be leakage.
|
||||||
|
*/
|
||||||
|
SMSMsgArray* smsproto_prepOutbound( SMSProto* state, const XP_U8* buf,
|
||||||
|
XP_U16 buflen, const XP_UCHAR* toPhone,
|
||||||
|
XP_Bool forceOld, XP_U16* waitSecs );
|
||||||
|
|
||||||
|
/* When a message is received, pass it in for reassambly. Non-null return
|
||||||
|
means one or more messages is ready for consumption. */
|
||||||
|
SMSMsgArray* smsproto_prepInbound( SMSProto* state, const XP_UCHAR* fromPhone,
|
||||||
|
const XP_U8* data, XP_U16 len );
|
||||||
|
|
||||||
|
void smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr );
|
||||||
|
|
||||||
|
# ifdef DEBUG
|
||||||
|
void smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil );
|
||||||
|
# endif
|
||||||
|
#endif
|
|
@ -179,6 +179,26 @@ stringToStream( XWStreamCtxt* stream, const XP_UCHAR* str )
|
||||||
stream_putBytes( stream, str, len );
|
stream_putBytes( stream, str, len );
|
||||||
} /* putStringToStream */
|
} /* putStringToStream */
|
||||||
|
|
||||||
|
XP_Bool
|
||||||
|
stream_gotU8( XWStreamCtxt* stream, XP_U8* ptr )
|
||||||
|
{
|
||||||
|
XP_Bool success = sizeof(*ptr) <= stream_getSize( stream );
|
||||||
|
if ( success ) {
|
||||||
|
*ptr = stream_getU8( stream );
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_Bool
|
||||||
|
stream_gotBytes( XWStreamCtxt* stream, void* ptr, XP_U16 len )
|
||||||
|
{
|
||||||
|
XP_Bool success = len <= stream_getSize( stream );
|
||||||
|
if ( success ) {
|
||||||
|
stream_getBytes( stream, ptr, len );
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
|
@ -56,6 +56,9 @@ XP_UCHAR* p_stringFromStream( MPFORMAL XWStreamCtxt* stream
|
||||||
XP_U16 stringFromStreamHere( XWStreamCtxt* stream, XP_UCHAR* buf, XP_U16 len );
|
XP_U16 stringFromStreamHere( XWStreamCtxt* stream, XP_UCHAR* buf, XP_U16 len );
|
||||||
void stringToStream( XWStreamCtxt* stream, const XP_UCHAR* str );
|
void stringToStream( XWStreamCtxt* stream, const XP_UCHAR* str );
|
||||||
|
|
||||||
|
XP_Bool stream_gotU8( XWStreamCtxt* stream, XP_U8* ptr );
|
||||||
|
XP_Bool stream_gotBytes( XWStreamCtxt* stream, void* ptr, XP_U16 len );
|
||||||
|
|
||||||
XP_UCHAR* p_copyString( MPFORMAL const XP_UCHAR* instr
|
XP_UCHAR* p_copyString( MPFORMAL const XP_UCHAR* instr
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
, const char* file, const char* func, XP_U32 lineNo
|
, const char* file, const char* func, XP_U32 lineNo
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 1997 - 2010 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 1997 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -26,9 +26,8 @@
|
||||||
#include "dawg.h"
|
#include "dawg.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "mempool.h"
|
|
||||||
#include "vtabmgr.h"
|
|
||||||
#include "comms.h"
|
#include "comms.h"
|
||||||
|
#include "dutil.h"
|
||||||
|
|
||||||
#include "xwrelay.h"
|
#include "xwrelay.h"
|
||||||
|
|
||||||
|
@ -87,8 +86,6 @@ typedef XP_Bool (*XWTimerProc)( void* closure, XWTimerReason why );
|
||||||
*/
|
*/
|
||||||
typedef struct UtilVtable {
|
typedef struct UtilVtable {
|
||||||
|
|
||||||
VTableMgr* (*m_util_getVTManager)(XW_UtilCtxt* uc);
|
|
||||||
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
XWStreamCtxt* (*m_util_makeStreamFromAddr )(XW_UtilCtxt* uc,
|
XWStreamCtxt* (*m_util_makeStreamFromAddr )(XW_UtilCtxt* uc,
|
||||||
XP_PlayerAddr channelNo );
|
XP_PlayerAddr channelNo );
|
||||||
|
@ -144,21 +141,8 @@ typedef struct UtilVtable {
|
||||||
void (*m_util_requestTime)( XW_UtilCtxt* uc );
|
void (*m_util_requestTime)( XW_UtilCtxt* uc );
|
||||||
|
|
||||||
XP_Bool (*m_util_altKeyDown)( XW_UtilCtxt* uc );
|
XP_Bool (*m_util_altKeyDown)( XW_UtilCtxt* uc );
|
||||||
|
|
||||||
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
|
|
||||||
#ifdef XWFEATURE_DEVID
|
|
||||||
const XP_UCHAR* (*m_util_getDevID)( XW_UtilCtxt* uc, DevIDType* typ );
|
|
||||||
void (*m_util_deviceRegistered)( XW_UtilCtxt* uc, DevIDType typ,
|
|
||||||
const XP_UCHAR* idRelay );
|
|
||||||
#endif
|
|
||||||
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
|
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
|
||||||
|
|
||||||
const XP_UCHAR* (*m_util_getUserString)( XW_UtilCtxt* uc,
|
|
||||||
XP_U16 stringCode );
|
|
||||||
const XP_UCHAR* (*m_util_getUserQuantityString)( XW_UtilCtxt* uc,
|
|
||||||
XP_U16 stringCode,
|
|
||||||
XP_U16 quantity );
|
|
||||||
|
|
||||||
void (*m_util_notifyIllegalWords)( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
void (*m_util_notifyIllegalWords)( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
||||||
XP_U16 turn, XP_Bool turnLost );
|
XP_U16 turn, XP_Bool turnLost );
|
||||||
|
|
||||||
|
@ -172,11 +156,6 @@ typedef struct UtilVtable {
|
||||||
void (*m_util_cellSquareHeld)( XW_UtilCtxt* uc, XWStreamCtxt* words );
|
void (*m_util_cellSquareHeld)( XW_UtilCtxt* uc, XWStreamCtxt* words );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
|
||||||
XP_Bool (*m_util_phoneNumbersSame)( XW_UtilCtxt* uc, const XP_UCHAR* p1,
|
|
||||||
const XP_UCHAR* p2 );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
void (*m_util_informMissing)(XW_UtilCtxt* uc, XP_Bool isServer,
|
void (*m_util_informMissing)(XW_UtilCtxt* uc, XP_Bool isServer,
|
||||||
const CommsAddrRec* addr, XP_U16 nDevs,
|
const CommsAddrRec* addr, XP_U16 nDevs,
|
||||||
|
@ -201,9 +180,7 @@ typedef struct UtilVtable {
|
||||||
void (*m_util_engineStopping)( XW_UtilCtxt* uc );
|
void (*m_util_engineStopping)( XW_UtilCtxt* uc );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef COMMS_CHECKSUM
|
XW_DUtilCtxt* (*m_util_getDevUtilCtxt)( XW_UtilCtxt* uc );
|
||||||
XP_UCHAR* (*m_util_md5sum)( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} UtilVtable;
|
} UtilVtable;
|
||||||
|
|
||||||
|
@ -217,9 +194,6 @@ struct XW_UtilCtxt {
|
||||||
MPSLOT
|
MPSLOT
|
||||||
};
|
};
|
||||||
|
|
||||||
#define util_getVTManager(uc) \
|
|
||||||
(uc)->vtable->m_util_getVTManager((uc))
|
|
||||||
|
|
||||||
#define util_makeStreamFromAddr(uc,a) \
|
#define util_makeStreamFromAddr(uc,a) \
|
||||||
(uc)->vtable->m_util_makeStreamFromAddr((uc),(a))
|
(uc)->vtable->m_util_makeStreamFromAddr((uc),(a))
|
||||||
|
|
||||||
|
@ -286,24 +260,9 @@ struct XW_UtilCtxt {
|
||||||
#define util_altKeyDown( uc ) \
|
#define util_altKeyDown( uc ) \
|
||||||
(uc)->vtable->m_util_altKeyDown((uc))
|
(uc)->vtable->m_util_altKeyDown((uc))
|
||||||
|
|
||||||
#define util_getCurSeconds(uc) \
|
|
||||||
(uc)->vtable->m_util_getCurSeconds((uc))
|
|
||||||
|
|
||||||
#ifdef XWFEATURE_DEVID
|
|
||||||
# define util_getDevID( uc, t ) \
|
|
||||||
(uc)->vtable->m_util_getDevID((uc),(t))
|
|
||||||
# define util_deviceRegistered( uc, typ, id ) \
|
|
||||||
(uc)->vtable->m_util_deviceRegistered( (uc), (typ), (id) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define util_makeEmptyDict( uc ) \
|
#define util_makeEmptyDict( uc ) \
|
||||||
(uc)->vtable->m_util_makeEmptyDict((uc))
|
(uc)->vtable->m_util_makeEmptyDict((uc))
|
||||||
|
|
||||||
#define util_getUserString( uc, c ) \
|
|
||||||
(uc)->vtable->m_util_getUserString((uc),(c))
|
|
||||||
#define util_getUserQuantityString( uc, c, q ) \
|
|
||||||
(uc)->vtable->m_util_getUserQuantityString((uc),(c),(q))
|
|
||||||
|
|
||||||
#define util_notifyIllegalWords( uc, w, p, b ) \
|
#define util_notifyIllegalWords( uc, w, p, b ) \
|
||||||
(uc)->vtable->m_util_notifyIllegalWords((uc),(w),(p),(b))
|
(uc)->vtable->m_util_notifyIllegalWords((uc),(w),(p),(b))
|
||||||
|
|
||||||
|
@ -320,10 +279,6 @@ struct XW_UtilCtxt {
|
||||||
#define util_cellSquareHeld(uc, s) \
|
#define util_cellSquareHeld(uc, s) \
|
||||||
(uc)->vtable->m_util_cellSquareHeld( (uc), (s) )
|
(uc)->vtable->m_util_cellSquareHeld( (uc), (s) )
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_SMS
|
|
||||||
#define util_phoneNumbersSame(uc,p1,p2) \
|
|
||||||
(uc)->vtable->m_util_phoneNumbersSame( (uc), (p1), (p2) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
# define util_informMissing( uc, is, ct, nd, nm ) \
|
# define util_informMissing( uc, is, ct, nd, nm ) \
|
||||||
|
@ -355,8 +310,7 @@ struct XW_UtilCtxt {
|
||||||
# define util_engineStopping( uc )
|
# define util_engineStopping( uc )
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#ifdef COMMS_CHECKSUM
|
# define util_getDevUtilCtxt(uc) \
|
||||||
# define util_md5sum( uc, p, l ) (uc)->vtable->m_util_md5sum((uc), (p), (l))
|
(uc)->vtable->m_util_getDevUtilCtxt( (uc) )
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
1
xwords4/linux/.gitignore
vendored
1
xwords4/linux/.gitignore
vendored
|
@ -8,3 +8,4 @@ discon_ok2.sh_logs
|
||||||
test_backsend.sh_*
|
test_backsend.sh_*
|
||||||
*.db
|
*.db
|
||||||
dawg2dict
|
dawg2dict
|
||||||
|
discon_ok2.py_logs
|
|
@ -225,6 +225,7 @@ OBJ = \
|
||||||
$(BUILD_PLAT_DIR)/linuxutl.o \
|
$(BUILD_PLAT_DIR)/linuxutl.o \
|
||||||
$(BUILD_PLAT_DIR)/gamesdb.o \
|
$(BUILD_PLAT_DIR)/gamesdb.o \
|
||||||
$(BUILD_PLAT_DIR)/relaycon.o \
|
$(BUILD_PLAT_DIR)/relaycon.o \
|
||||||
|
$(BUILD_PLAT_DIR)/lindutil.o \
|
||||||
$(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS)
|
$(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS)
|
||||||
|
|
||||||
LIBS = -lm -lpthread -luuid -lcurl -ljson-c $(GPROFFLAG)
|
LIBS = -lm -lpthread -luuid -lcurl -ljson-c $(GPROFFLAG)
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include "linuxudp.h"
|
#include "linuxudp.h"
|
||||||
#include "gamesdb.h"
|
#include "gamesdb.h"
|
||||||
#include "relaycon.h"
|
#include "relaycon.h"
|
||||||
|
#include "smsproto.h"
|
||||||
|
|
||||||
#ifdef CURSES_SMALL_SCREEN
|
#ifdef CURSES_SMALL_SCREEN
|
||||||
# define MENU_WINDOW_HEIGHT 1
|
# define MENU_WINDOW_HEIGHT 1
|
||||||
|
@ -346,9 +347,8 @@ cursesShowFinalScores( CursesAppGlobals* globals )
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
XP_UCHAR* text;
|
XP_UCHAR* text;
|
||||||
|
|
||||||
stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
|
stream = mem_stream_make_raw( MPPARM(globals->cGlobals.util->mpool)
|
||||||
globals->cGlobals.params->vtMgr,
|
globals->cGlobals.params->vtMgr );
|
||||||
globals, CHANNEL_NONE, NULL );
|
|
||||||
server_writeFinalScores( globals->cGlobals.game.server, stream );
|
server_writeFinalScores( globals->cGlobals.game.server, stream );
|
||||||
|
|
||||||
text = strFromStream( stream );
|
text = strFromStream( stream );
|
||||||
|
@ -1363,13 +1363,6 @@ initClientSocket( CursesAppGlobals* globals, char* serverName )
|
||||||
} /* initClientSocket */
|
} /* initClientSocket */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static VTableMgr*
|
|
||||||
curses_util_getVTManager(XW_UtilCtxt* uc)
|
|
||||||
{
|
|
||||||
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
|
|
||||||
return globals->cGlobals.params->vtMgr;
|
|
||||||
} /* linux_util_getVTManager */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc),
|
curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc),
|
||||||
XP_U16 XP_UNUSED_DBG(playerNum),
|
XP_U16 XP_UNUSED_DBG(playerNum),
|
||||||
|
@ -1413,9 +1406,8 @@ curses_util_remSelected( XW_UtilCtxt* uc )
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
XP_UCHAR* text;
|
XP_UCHAR* text;
|
||||||
|
|
||||||
stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
|
stream = mem_stream_make_raw( MPPARM(globals->cGlobals.util->mpool)
|
||||||
globals->cGlobals.params->vtMgr,
|
globals->cGlobals.params->vtMgr );
|
||||||
globals, CHANNEL_NONE, NULL );
|
|
||||||
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
||||||
|
|
||||||
text = strFromStream( stream );
|
text = strFromStream( stream );
|
||||||
|
@ -1464,7 +1456,6 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
|
||||||
{
|
{
|
||||||
util->vtable->m_util_userError = curses_util_userError;
|
util->vtable->m_util_userError = curses_util_userError;
|
||||||
|
|
||||||
util->vtable->m_util_getVTManager = curses_util_getVTManager;
|
|
||||||
util->vtable->m_util_informNeedPassword = curses_util_informNeedPassword;
|
util->vtable->m_util_informNeedPassword = curses_util_informNeedPassword;
|
||||||
util->vtable->m_util_yOffsetChange = curses_util_yOffsetChange;
|
util->vtable->m_util_yOffsetChange = curses_util_yOffsetChange;
|
||||||
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
||||||
|
@ -1740,7 +1731,8 @@ curses_requestMsgs( gpointer data )
|
||||||
{
|
{
|
||||||
CursesAppGlobals* globals = (CursesAppGlobals*)data;
|
CursesAppGlobals* globals = (CursesAppGlobals*)data;
|
||||||
XP_UCHAR devIDBuf[64] = {0};
|
XP_UCHAR devIDBuf[64] = {0};
|
||||||
db_fetch( globals->cGlobals.pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
db_fetch_safe( globals->cGlobals.params->pDb, KEY_RDEVID, devIDBuf,
|
||||||
|
sizeof(devIDBuf) );
|
||||||
if ( '\0' != devIDBuf[0] ) {
|
if ( '\0' != devIDBuf[0] ) {
|
||||||
relaycon_requestMsgs( globals->cGlobals.params, devIDBuf );
|
relaycon_requestMsgs( globals->cGlobals.params, devIDBuf );
|
||||||
} else {
|
} else {
|
||||||
|
@ -1772,13 +1764,13 @@ cursesDevIDReceived( void* closure, const XP_UCHAR* devID,
|
||||||
{
|
{
|
||||||
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
|
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
|
||||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||||
sqlite3* pDb = cGlobals->pDb;
|
sqlite3* pDb = cGlobals->params->pDb;
|
||||||
if ( !!devID ) {
|
if ( !!devID ) {
|
||||||
XP_LOGF( "%s(devID=%s)", __func__, devID );
|
XP_LOGF( "%s(devID=%s)", __func__, devID );
|
||||||
|
|
||||||
/* If we already have one, make sure it's the same! Else store. */
|
/* If we already have one, make sure it's the same! Else store. */
|
||||||
gchar buf[64];
|
gchar buf[64];
|
||||||
XP_Bool have = db_fetch( pDb, KEY_RDEVID, buf, sizeof(buf) )
|
XP_Bool have = db_fetch_safe( pDb, KEY_RDEVID, buf, sizeof(buf) )
|
||||||
&& 0 == strcmp( buf, devID );
|
&& 0 == strcmp( buf, devID );
|
||||||
if ( !have ) {
|
if ( !have ) {
|
||||||
db_store( pDb, KEY_RDEVID, devID );
|
db_store( pDb, KEY_RDEVID, devID );
|
||||||
|
@ -1998,9 +1990,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
|
|
||||||
XP_Bool idIsNew = XP_TRUE;
|
XP_Bool idIsNew = XP_TRUE;
|
||||||
if ( !!params->dbName ) {
|
if ( !!params->dbName ) {
|
||||||
sqlite3* pDb = openGamesDB( params->dbName );
|
params->pDb = openGamesDB( params->dbName );
|
||||||
/* Gross that both need to be set, but they do. */
|
|
||||||
params->pDb = g_globals.cGlobals.pDb = pDb;
|
|
||||||
|
|
||||||
/* Check if we have a local ID already. If we do and it's
|
/* Check if we have a local ID already. If we do and it's
|
||||||
changed, we care. */
|
changed, we care. */
|
||||||
|
@ -2026,18 +2016,18 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
gchar buf[32];
|
gchar buf[32];
|
||||||
const gchar* myPhone = params->connInfo.sms.phone;
|
const gchar* myPhone = params->connInfo.sms.myPhone;
|
||||||
if ( !!myPhone ) {
|
if ( !!myPhone ) {
|
||||||
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
||||||
} else if ( !myPhone && db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
} else if ( !myPhone && db_fetch_safe( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||||
params->connInfo.sms.phone = myPhone = buf;
|
params->connInfo.sms.myPhone = myPhone = buf;
|
||||||
}
|
}
|
||||||
XP_U16 myPort = params->connInfo.sms.port;
|
XP_U16 myPort = params->connInfo.sms.port;
|
||||||
gchar portbuf[8];
|
gchar portbuf[8];
|
||||||
if ( 0 < myPort ) {
|
if ( 0 < myPort ) {
|
||||||
sprintf( portbuf, "%d", myPort );
|
sprintf( portbuf, "%d", myPort );
|
||||||
db_store( params->pDb, KEY_SMSPORT, portbuf );
|
db_store( params->pDb, KEY_SMSPORT, portbuf );
|
||||||
} else if ( db_fetch( params->pDb, KEY_SMSPORT, portbuf, VSIZE(portbuf) ) ) {
|
} else if ( db_fetch_safe( params->pDb, KEY_SMSPORT, portbuf, VSIZE(portbuf) ) ) {
|
||||||
params->connInfo.sms.port = myPort = atoi( portbuf );
|
params->connInfo.sms.port = myPort = atoi( portbuf );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2049,6 +2039,11 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
};
|
};
|
||||||
linux_sms_init( params, myPhone, myPort, &smsProcs, &g_globals.cGlobals );
|
linux_sms_init( params, myPhone, myPort, &smsProcs, &g_globals.cGlobals );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( params->runSMSTest ) {
|
||||||
|
smsproto_runTests(g_globals.cGlobals.util->mpool,
|
||||||
|
g_globals.cGlobals.params->dutil );
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
XWStreamCtxt* stream = NULL;
|
XWStreamCtxt* stream = NULL;
|
||||||
|
@ -2056,9 +2051,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
GSList* games = listGames( params->pDb );
|
GSList* games = listGames( params->pDb );
|
||||||
if ( !!games ) {
|
if ( !!games ) {
|
||||||
XP_ASSERT( 1 == g_slist_length(games) ); /* for now */
|
XP_ASSERT( 1 == g_slist_length(games) ); /* for now */
|
||||||
stream = mem_stream_make( MEMPOOL params->vtMgr,
|
stream = mem_stream_make_raw( MEMPOOL params->vtMgr);
|
||||||
&g_globals.cGlobals, CHANNEL_NONE,
|
|
||||||
NULL );
|
|
||||||
sqlite3_int64 selRow = *(sqlite3_int64*)games->data;
|
sqlite3_int64 selRow = *(sqlite3_int64*)games->data;
|
||||||
/* XP_UCHAR buf[32]; */
|
/* XP_UCHAR buf[32]; */
|
||||||
/* XP_SNPRINTF( buf, sizeof(buf), "%lld", selRow ); */
|
/* XP_SNPRINTF( buf, sizeof(buf), "%lld", selRow ); */
|
||||||
|
@ -2074,14 +2067,13 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
|
|
||||||
} else if ( !!params->fileName && file_exists( params->fileName ) ) {
|
} else if ( !!params->fileName && file_exists( params->fileName ) ) {
|
||||||
mpool_setTag( MEMPOOL "file" );
|
mpool_setTag( MEMPOOL "file" );
|
||||||
stream = streamFromFile( &g_globals.cGlobals, params->fileName,
|
stream = streamFromFile( &g_globals.cGlobals, params->fileName );
|
||||||
&g_globals );
|
|
||||||
#ifdef USE_SQLITE
|
#ifdef USE_SQLITE
|
||||||
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
||||||
XP_UCHAR buf[32];
|
XP_UCHAR buf[32];
|
||||||
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
||||||
mpool_setTag( MEMPOOL buf );
|
mpool_setTag( MEMPOOL buf );
|
||||||
stream = streamFromDB( &g_globals.cGlobals, &g_globals );
|
stream = streamFromDB( &g_globals.cGlobals );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2141,7 +2133,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
# ifdef XWFEATURE_SMS
|
# ifdef XWFEATURE_SMS
|
||||||
case COMMS_CONN_SMS:
|
case COMMS_CONN_SMS:
|
||||||
addr_addType( &addr, COMMS_CONN_SMS );
|
addr_addType( &addr, COMMS_CONN_SMS );
|
||||||
XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.phone,
|
XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.myPhone,
|
||||||
sizeof(addr.u.sms.phone) - 1 );
|
sizeof(addr.u.sms.phone) - 1 );
|
||||||
addr.u.sms.port = params->connInfo.sms.port;
|
addr.u.sms.port = params->connInfo.sms.port;
|
||||||
break;
|
break;
|
||||||
|
@ -2238,7 +2230,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
endwin();
|
endwin();
|
||||||
|
|
||||||
if ( !!params->dbName ) {
|
if ( !!params->dbName ) {
|
||||||
closeGamesDB( g_globals.cGlobals.pDb );
|
closeGamesDB( params->pDb );
|
||||||
}
|
}
|
||||||
relaycon_cleanup( params );
|
relaycon_cleanup( params );
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
#define SNAP_WIDTH 150
|
#define SNAP_WIDTH 150
|
||||||
#define SNAP_HEIGHT 150
|
#define SNAP_HEIGHT 150
|
||||||
|
|
||||||
static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
static XP_Bool getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
||||||
int len );
|
int* len );
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static char* sqliteErr2str( int err );
|
static char* sqliteErr2str( int err );
|
||||||
#endif
|
#endif
|
||||||
|
@ -160,13 +161,16 @@ writeToDB( XWStreamCtxt* stream, void* closure )
|
||||||
{
|
{
|
||||||
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
||||||
sqlite3_int64 selRow = cGlobals->selRow;
|
sqlite3_int64 selRow = cGlobals->selRow;
|
||||||
sqlite3* pDb = cGlobals->pDb;
|
sqlite3* pDb = cGlobals->params->pDb;
|
||||||
|
|
||||||
XP_Bool newGame = -1 == selRow;
|
XP_Bool newGame = -1 == selRow;
|
||||||
selRow = writeBlobColumnStream( stream, pDb, selRow, "game" );
|
selRow = writeBlobColumnStream( stream, pDb, selRow, "game" );
|
||||||
|
|
||||||
if ( newGame ) { /* new row; need to insert blob first */
|
if ( newGame ) { /* new row; need to insert blob first */
|
||||||
cGlobals->selRow = selRow;
|
cGlobals->selRow = selRow;
|
||||||
|
XP_LOGF( "%s(): new game at row %lld", __func__, selRow );
|
||||||
|
} else {
|
||||||
|
assert( selRow == cGlobals->selRow );
|
||||||
}
|
}
|
||||||
|
|
||||||
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
|
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
|
||||||
|
@ -184,11 +188,12 @@ addSnapshot( CommonGlobals* cGlobals )
|
||||||
addSurface( dctx, SNAP_WIDTH, SNAP_HEIGHT );
|
addSurface( dctx, SNAP_WIDTH, SNAP_HEIGHT );
|
||||||
board_drawSnapshot( board, (DrawCtx*)dctx, SNAP_WIDTH, SNAP_HEIGHT );
|
board_drawSnapshot( board, (DrawCtx*)dctx, SNAP_WIDTH, SNAP_HEIGHT );
|
||||||
|
|
||||||
XWStreamCtxt* stream = make_simple_stream( cGlobals );
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
|
cGlobals->params->vtMgr );
|
||||||
getImage( dctx, stream );
|
getImage( dctx, stream );
|
||||||
removeSurface( dctx );
|
removeSurface( dctx );
|
||||||
writeBlobColumnStream( stream, cGlobals->pDb, cGlobals->selRow, "snap" );
|
cGlobals->selRow = writeBlobColumnStream( stream, cGlobals->params->pDb,
|
||||||
// XP_ASSERT( cGlobals->selRow == newRow );
|
cGlobals->selRow, "snap" );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,8 +269,8 @@ summarize( CommonGlobals* cGlobals )
|
||||||
cGlobals->selRow );
|
cGlobals->selRow );
|
||||||
XP_LOGF( "query: %s", buf );
|
XP_LOGF( "query: %s", buf );
|
||||||
sqlite3_stmt* stmt = NULL;
|
sqlite3_stmt* stmt = NULL;
|
||||||
int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL );
|
int result = sqlite3_prepare_v2( cGlobals->params->pDb, buf, -1, &stmt, NULL );
|
||||||
assertPrintResult( cGlobals->pDb, result, SQLITE_OK );
|
assertPrintResult( cGlobals->params->pDb, result, SQLITE_OK );
|
||||||
result = sqlite3_step( stmt );
|
result = sqlite3_step( stmt );
|
||||||
if ( SQLITE_DONE != result ) {
|
if ( SQLITE_DONE != result ) {
|
||||||
XP_LOGF( "sqlite3_step=>%s", sqliteErr2str( result ) );
|
XP_LOGF( "sqlite3_step=>%s", sqliteErr2str( result ) );
|
||||||
|
@ -337,7 +342,8 @@ getRelayIDsToRowsMap( sqlite3* pDb )
|
||||||
case SQLITE_ROW: /* have data */
|
case SQLITE_ROW: /* have data */
|
||||||
{
|
{
|
||||||
XP_UCHAR relayID[32];
|
XP_UCHAR relayID[32];
|
||||||
getColumnText( ppStmt, 0, relayID, VSIZE(relayID) );
|
int len = VSIZE(relayID);
|
||||||
|
getColumnText( ppStmt, 0, relayID, &len );
|
||||||
gpointer key = g_strdup( relayID );
|
gpointer key = g_strdup( relayID );
|
||||||
sqlite3_int64* value = g_malloc( sizeof( value ) );
|
sqlite3_int64* value = g_malloc( sizeof( value ) );
|
||||||
*value = sqlite3_column_int64( ppStmt, 1 );
|
*value = sqlite3_column_int64( ppStmt, 1 );
|
||||||
|
@ -375,7 +381,8 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
|
||||||
if ( SQLITE_ROW == result ) {
|
if ( SQLITE_ROW == result ) {
|
||||||
success = XP_TRUE;
|
success = XP_TRUE;
|
||||||
int col = 0;
|
int col = 0;
|
||||||
getColumnText( ppStmt, col++, gib->room, sizeof(gib->room) );
|
int len = sizeof(gib->room);
|
||||||
|
getColumnText( ppStmt, col++, gib->room, &len );
|
||||||
gib->gameOver = 1 == sqlite3_column_int( ppStmt, col++ );
|
gib->gameOver = 1 == sqlite3_column_int( ppStmt, col++ );
|
||||||
gib->turn = sqlite3_column_int( ppStmt, col++ );
|
gib->turn = sqlite3_column_int( ppStmt, col++ );
|
||||||
gib->turnLocal = 1 == sqlite3_column_int( ppStmt, col++ );
|
gib->turnLocal = 1 == sqlite3_column_int( ppStmt, col++ );
|
||||||
|
@ -383,10 +390,12 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
|
||||||
gib->nTotal = sqlite3_column_int( ppStmt, col++ );
|
gib->nTotal = sqlite3_column_int( ppStmt, col++ );
|
||||||
gib->nMissing = sqlite3_column_int( ppStmt, col++ );
|
gib->nMissing = sqlite3_column_int( ppStmt, col++ );
|
||||||
gib->seed = sqlite3_column_int( ppStmt, col++ );
|
gib->seed = sqlite3_column_int( ppStmt, col++ );
|
||||||
getColumnText( ppStmt, col++, gib->conn, sizeof(gib->conn) );
|
len = sizeof(gib->conn);
|
||||||
|
getColumnText( ppStmt, col++, gib->conn, &len );
|
||||||
gib->gameID = sqlite3_column_int( ppStmt, col++ );
|
gib->gameID = sqlite3_column_int( ppStmt, col++ );
|
||||||
gib->lastMoveTime = sqlite3_column_int( ppStmt, col++ );
|
gib->lastMoveTime = sqlite3_column_int( ppStmt, col++ );
|
||||||
getColumnText( ppStmt, col++, gib->relayID, sizeof(gib->relayID) );
|
len = sizeof(gib->relayID);
|
||||||
|
getColumnText( ppStmt, col++, gib->relayID, &len );
|
||||||
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
|
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
|
||||||
|
|
||||||
#ifdef PLATFORM_GTK
|
#ifdef PLATFORM_GTK
|
||||||
|
@ -488,6 +497,7 @@ loadInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
|
||||||
void
|
void
|
||||||
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
||||||
{
|
{
|
||||||
|
XP_ASSERT( !!pDb );
|
||||||
char query[256];
|
char query[256];
|
||||||
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
|
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
|
||||||
sqlite3_stmt* ppStmt;
|
sqlite3_stmt* ppStmt;
|
||||||
|
@ -502,9 +512,9 @@ deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
||||||
void
|
void
|
||||||
db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
||||||
{
|
{
|
||||||
char buf[256];
|
XP_ASSERT( !!pDb );
|
||||||
snprintf( buf, sizeof(buf),
|
gchar* buf =
|
||||||
"INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
g_strdup_printf( "INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
||||||
key, value );
|
key, value );
|
||||||
sqlite3_stmt *ppStmt;
|
sqlite3_stmt *ppStmt;
|
||||||
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
||||||
|
@ -513,33 +523,51 @@ db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
||||||
assertPrintResult( pDb, result, SQLITE_DONE );
|
assertPrintResult( pDb, result, SQLITE_DONE );
|
||||||
XP_USE( result );
|
XP_USE( result );
|
||||||
sqlite3_finalize( ppStmt );
|
sqlite3_finalize( ppStmt );
|
||||||
|
g_free( buf );
|
||||||
}
|
}
|
||||||
|
|
||||||
XP_Bool
|
FetchResult
|
||||||
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen )
|
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint* buflen )
|
||||||
{
|
{
|
||||||
|
XP_ASSERT( !!pDb );
|
||||||
|
FetchResult result = NOT_THERE;
|
||||||
char query[256];
|
char query[256];
|
||||||
snprintf( query, sizeof(query),
|
snprintf( query, sizeof(query),
|
||||||
"SELECT value from pairs where key = '%s'", key );
|
"SELECT value from pairs where key = '%s'", key );
|
||||||
sqlite3_stmt *ppStmt;
|
sqlite3_stmt *ppStmt;
|
||||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
int sqlResult = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||||
XP_Bool found = SQLITE_OK == result;
|
XP_Bool found = SQLITE_OK == sqlResult;
|
||||||
if ( found ) {
|
if ( found ) {
|
||||||
result = sqlite3_step( ppStmt );
|
result = sqlite3_step( ppStmt );
|
||||||
found = SQLITE_ROW == result;
|
found = SQLITE_ROW == result;
|
||||||
if ( found ) {
|
if ( found ) {
|
||||||
getColumnText( ppStmt, 0, buf, buflen );
|
if ( getColumnText( ppStmt, 0, buf, buflen ) ) {
|
||||||
|
result = SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
|
result = BUFFER_TOO_SMALL;
|
||||||
|
}
|
||||||
|
} else if ( !!buf ) {
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_finalize( ppStmt );
|
sqlite3_finalize( ppStmt );
|
||||||
return found;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_Bool
|
||||||
|
db_fetch_safe( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen )
|
||||||
|
{
|
||||||
|
XP_ASSERT( !!pDb );
|
||||||
|
int tmp = buflen;
|
||||||
|
FetchResult result = db_fetch( pDb, key, buf, &tmp );
|
||||||
|
XP_ASSERT( result != BUFFER_TOO_SMALL );
|
||||||
|
return SUCCESS == result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
db_remove( sqlite3* pDb, const gchar* key )
|
db_remove( sqlite3* pDb, const gchar* key )
|
||||||
{
|
{
|
||||||
|
XP_ASSERT( !!pDb );
|
||||||
char query[256];
|
char query[256];
|
||||||
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
|
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
|
||||||
sqlite3_stmt *ppStmt;
|
sqlite3_stmt *ppStmt;
|
||||||
|
@ -551,15 +579,19 @@ db_remove( sqlite3* pDb, const gchar* key )
|
||||||
sqlite3_finalize( ppStmt );
|
sqlite3_finalize( ppStmt );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static XP_Bool
|
||||||
getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf, int *len )
|
||||||
int XP_UNUSED_DBG(len) )
|
|
||||||
{
|
{
|
||||||
const unsigned char* txt = sqlite3_column_text( ppStmt, iCol );
|
int colLen = sqlite3_column_bytes( ppStmt, iCol );
|
||||||
int needLen = sqlite3_column_bytes( ppStmt, iCol );
|
|
||||||
XP_ASSERT( needLen < len );
|
XP_Bool success = colLen < *len;
|
||||||
XP_MEMCPY( buf, txt, needLen );
|
*len = colLen + 1; /* sqlite does not store the null byte */
|
||||||
buf[needLen] = '\0';
|
if ( success ) {
|
||||||
|
const unsigned char* colTxt = sqlite3_column_text( ppStmt, iCol );
|
||||||
|
XP_MEMCPY( buf, colTxt, colLen );
|
||||||
|
buf[colLen] = '\0';
|
||||||
|
}
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
|
@ -47,7 +47,7 @@ typedef struct _GameInfo {
|
||||||
} GameInfo;
|
} GameInfo;
|
||||||
|
|
||||||
sqlite3* openGamesDB( const char* dbName );
|
sqlite3* openGamesDB( const char* dbName );
|
||||||
void closeGamesDB( sqlite3* dbp );
|
void closeGamesDB( sqlite3* pDb );
|
||||||
|
|
||||||
void writeToDB( XWStreamCtxt* stream, void* closure );
|
void writeToDB( XWStreamCtxt* stream, void* closure );
|
||||||
sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
|
sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
|
||||||
|
@ -55,15 +55,15 @@ sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
|
||||||
void summarize( CommonGlobals* cGlobals );
|
void summarize( CommonGlobals* cGlobals );
|
||||||
|
|
||||||
/* Return GSList whose data is (ptrs to) rowids */
|
/* Return GSList whose data is (ptrs to) rowids */
|
||||||
GSList* listGames( sqlite3* dbp );
|
GSList* listGames( sqlite3* pDb );
|
||||||
/* free list and data allocated by above */
|
/* free list and data allocated by above */
|
||||||
void freeGamesList( GSList* games );
|
void freeGamesList( GSList* games );
|
||||||
|
|
||||||
/* Mapping of relayID -> rowid */
|
/* Mapping of relayID -> rowid */
|
||||||
GHashTable* getRelayIDsToRowsMap( sqlite3* pDb );
|
GHashTable* getRelayIDsToRowsMap( sqlite3* pDb );
|
||||||
|
|
||||||
XP_Bool getGameInfo( sqlite3* dbp, sqlite3_int64 rowid, GameInfo* gib );
|
XP_Bool getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib );
|
||||||
void getRowsForGameID( sqlite3* dbp, XP_U32 gameID, sqlite3_int64* rowids,
|
void getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids,
|
||||||
int* nRowIDs );
|
int* nRowIDs );
|
||||||
XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid );
|
XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid );
|
||||||
void saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb,
|
void saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb,
|
||||||
|
@ -78,8 +78,12 @@ void deleteGame( sqlite3* pDb, sqlite3_int64 rowid );
|
||||||
#define KEY_SMSPORT "SMSPORT"
|
#define KEY_SMSPORT "SMSPORT"
|
||||||
#define KEY_WIN_LOC "WIN_LOC"
|
#define KEY_WIN_LOC "WIN_LOC"
|
||||||
|
|
||||||
void db_store( sqlite3* dbp, const gchar* key, const gchar* value );
|
void db_store( sqlite3* pDb, const gchar* key, const gchar* value );
|
||||||
XP_Bool db_fetch( sqlite3* dbp, const gchar* key, gchar* buf, gint buflen );
|
void db_remove( sqlite3* pDb, const gchar* key );
|
||||||
void db_remove( sqlite3* dbp, const gchar* key );
|
|
||||||
|
typedef enum { NOT_THERE, BUFFER_TOO_SMALL, SUCCESS } FetchResult;
|
||||||
|
FetchResult db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint* buflen );
|
||||||
|
XP_Bool db_fetch_safe( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen );
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -86,7 +86,7 @@ static void gtkShowFinalScores( const GtkGameGlobals* globals,
|
||||||
XP_Bool ignoreTimeout );
|
XP_Bool ignoreTimeout );
|
||||||
static void send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
static void send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||||
XP_U32 devID, const XP_UCHAR* relayID,
|
XP_U32 devID, const XP_UCHAR* relayID,
|
||||||
const XP_UCHAR* phone );
|
const CommsAddrRec* addrs );
|
||||||
|
|
||||||
|
|
||||||
#define GTK_TRAY_HT_ROWS 3
|
#define GTK_TRAY_HT_ROWS 3
|
||||||
|
@ -589,19 +589,18 @@ createOrLoadObjects( GtkGameGlobals* globals )
|
||||||
setTransportProcs( &procs, globals );
|
setTransportProcs( &procs, globals );
|
||||||
|
|
||||||
if ( !!params->fileName && file_exists( params->fileName ) ) {
|
if ( !!params->fileName && file_exists( params->fileName ) ) {
|
||||||
stream = streamFromFile( cGlobals, params->fileName, globals );
|
stream = streamFromFile( cGlobals, params->fileName );
|
||||||
#ifdef USE_SQLITE
|
#ifdef USE_SQLITE
|
||||||
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
||||||
XP_UCHAR buf[32];
|
XP_UCHAR buf[32];
|
||||||
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
||||||
mpool_setTag( MEMPOOL buf );
|
mpool_setTag( MEMPOOL buf );
|
||||||
stream = streamFromDB( cGlobals, globals );
|
stream = streamFromDB( cGlobals );
|
||||||
#endif
|
#endif
|
||||||
} else if ( !!cGlobals->pDb && 0 <= cGlobals->selRow ) {
|
} else if ( !!params->pDb && 0 <= cGlobals->selRow ) {
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
params->vtMgr,
|
params->vtMgr );
|
||||||
cGlobals, CHANNEL_NONE, NULL );
|
if ( !loadGame( stream, params->pDb, cGlobals->selRow ) ) {
|
||||||
if ( !loadGame( stream, cGlobals->pDb, cGlobals->selRow ) ) {
|
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
}
|
}
|
||||||
|
@ -874,10 +873,9 @@ on_board_window_shown( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
||||||
CommsCtxt* comms = cGlobals->game.comms;
|
CommsCtxt* comms = cGlobals->game.comms;
|
||||||
if ( !!comms /*&& COMMS_CONN_NONE == comms_getConTypes( comms )*/ ) {
|
if ( !!comms /*&& COMMS_CONN_NONE == comms_getConTypes( comms )*/ ) {
|
||||||
/* If it has pending invite info, send the invitation! */
|
/* If it has pending invite info, send the invitation! */
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
cGlobals->params->vtMgr );
|
||||||
cGlobals, CHANNEL_NONE, NULL );
|
if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->selRow ) ) {
|
||||||
if ( loadInviteAddrs( stream, cGlobals->pDb, cGlobals->selRow ) ) {
|
|
||||||
CommsAddrRec addr = {0};
|
CommsAddrRec addr = {0};
|
||||||
addrFromStream( &addr, stream );
|
addrFromStream( &addr, stream );
|
||||||
comms_setAddr( cGlobals->game.comms, &addr );
|
comms_setAddr( cGlobals->game.comms, &addr );
|
||||||
|
@ -1622,21 +1620,21 @@ handle_invite_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
||||||
XP_LOGF( "%s: inviteDlg => %d", __func__, confirmed );
|
XP_LOGF( "%s: inviteDlg => %d", __func__, confirmed );
|
||||||
|
|
||||||
if ( confirmed ) {
|
if ( confirmed ) {
|
||||||
send_invites( cGlobals, nPlayers, devID, NULL, NULL );
|
send_invites( cGlobals, nPlayers, devID, NULL, &inviteAddr );
|
||||||
}
|
}
|
||||||
} /* handle_invite_button */
|
} /* handle_invite_button */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||||
XP_U32 devID, const XP_UCHAR* relayID,
|
XP_U32 devID, const XP_UCHAR* relayID,
|
||||||
const XP_UCHAR* phone )
|
const CommsAddrRec* addrs )
|
||||||
{
|
{
|
||||||
CommsAddrRec addr = {0};
|
CommsAddrRec addr = {0};
|
||||||
CommsCtxt* comms = cGlobals->game.comms;
|
CommsCtxt* comms = cGlobals->game.comms;
|
||||||
XP_ASSERT( comms );
|
XP_ASSERT( comms );
|
||||||
comms_getAddr( comms, &addr );
|
comms_getAddr( comms, &addr );
|
||||||
|
|
||||||
gint forceChannel = 0; /* PENDING */
|
gint forceChannel = 1; /* 1 is what Android does. Limits to two-device games */
|
||||||
|
|
||||||
NetLaunchInfo nli = {0};
|
NetLaunchInfo nli = {0};
|
||||||
nli_init( &nli, cGlobals->gi, &addr, nPlayers, forceChannel );
|
nli_init( &nli, cGlobals->gi, &addr, nPlayers, forceChannel );
|
||||||
|
@ -1647,9 +1645,8 @@ send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
cGlobals->params->vtMgr );
|
||||||
NULL, CHANNEL_NONE, NULL );
|
|
||||||
nli_saveToStream( &nli, stream );
|
nli_saveToStream( &nli, stream );
|
||||||
NetLaunchInfo tmp;
|
NetLaunchInfo tmp;
|
||||||
nli_makeFromStream( &tmp, stream );
|
nli_makeFromStream( &tmp, stream );
|
||||||
|
@ -1658,11 +1655,13 @@ send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( !!phone ) {
|
if ( !!addrs->u.sms.phone ) {
|
||||||
XP_ASSERT( 0 ); /* not implemented */
|
gchar gameName[64];
|
||||||
/* linux_sms_invite( cGlobals->params, gi, &addr, gameName, */
|
snprintf( gameName, VSIZE(gameName), "Game %d", cGlobals->gi->gameID );
|
||||||
/* nPlayers, forceChannel, */
|
|
||||||
/* inviteAddr.u.sms.phone, inviteAddr.u.sms.port ); */
|
linux_sms_invite( cGlobals->params, cGlobals->gi, &addr, gameName,
|
||||||
|
nPlayers, forceChannel,
|
||||||
|
addrs->u.sms.phone, addrs->u.sms.port );
|
||||||
}
|
}
|
||||||
if ( 0 != devID || !!relayID ) {
|
if ( 0 != devID || !!relayID ) {
|
||||||
XP_ASSERT( 0 != devID || (!!relayID && !!relayID[0]) );
|
XP_ASSERT( 0 != devID || (!!relayID && !!relayID[0]) );
|
||||||
|
@ -1722,14 +1721,6 @@ gtkUserError( GtkGameGlobals* globals, const char* format, ... )
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
} /* gtkUserError */
|
} /* gtkUserError */
|
||||||
|
|
||||||
static VTableMgr*
|
|
||||||
gtk_util_getVTManager(XW_UtilCtxt* uc)
|
|
||||||
{
|
|
||||||
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
|
||||||
return globals->cGlobals.params->vtMgr;
|
|
||||||
} /* linux_util_getVTManager */
|
|
||||||
|
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
ask_blank( gpointer data )
|
ask_blank( gpointer data )
|
||||||
{
|
{
|
||||||
|
@ -1934,9 +1925,8 @@ gtkShowFinalScores( const GtkGameGlobals* globals, XP_Bool ignoreTimeout )
|
||||||
XP_UCHAR* text;
|
XP_UCHAR* text;
|
||||||
const CommonGlobals* cGlobals = &globals->cGlobals;
|
const CommonGlobals* cGlobals = &globals->cGlobals;
|
||||||
|
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
cGlobals->params->vtMgr );
|
||||||
NULL, CHANNEL_NONE, NULL );
|
|
||||||
server_writeFinalScores( cGlobals->game.server, stream );
|
server_writeFinalScores( cGlobals->game.server, stream );
|
||||||
|
|
||||||
text = strFromStream( stream );
|
text = strFromStream( stream );
|
||||||
|
@ -2272,9 +2262,8 @@ gtk_util_remSelected( XW_UtilCtxt* uc )
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
XP_UCHAR* text;
|
XP_UCHAR* text;
|
||||||
|
|
||||||
stream = mem_stream_make( MEMPOOL
|
stream = mem_stream_make_raw( MEMPOOL
|
||||||
globals->cGlobals.params->vtMgr,
|
globals->cGlobals.params->vtMgr );
|
||||||
globals, CHANNEL_NONE, NULL );
|
|
||||||
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
||||||
text = strFromStream( stream );
|
text = strFromStream( stream );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
@ -2570,7 +2559,6 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
|
||||||
util->vtable->m_util_userError = gtk_util_userError;
|
util->vtable->m_util_userError = gtk_util_userError;
|
||||||
util->vtable->m_util_notifyMove = gtk_util_notifyMove;
|
util->vtable->m_util_notifyMove = gtk_util_notifyMove;
|
||||||
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
|
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
|
||||||
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
|
|
||||||
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
|
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
|
||||||
util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles;
|
util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles;
|
||||||
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
|
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
|
||||||
|
@ -2935,11 +2923,9 @@ loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
|
||||||
|
|
||||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||||
cGlobals->selRow = rowid;
|
cGlobals->selRow = rowid;
|
||||||
cGlobals->pDb = pDb;
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
params->vtMgr );
|
||||||
params->vtMgr, cGlobals,
|
XP_Bool loaded = loadGame( stream, pDb, rowid );
|
||||||
CHANNEL_NONE, NULL );
|
|
||||||
XP_Bool loaded = loadGame( stream, cGlobals->pDb, rowid );
|
|
||||||
if ( loaded ) {
|
if ( loaded ) {
|
||||||
if ( NULL == cGlobals->dict ) {
|
if ( NULL == cGlobals->dict ) {
|
||||||
cGlobals->dict = makeDictForStream( cGlobals, stream );
|
cGlobals->dict = makeDictForStream( cGlobals, stream );
|
||||||
|
|
|
@ -305,7 +305,7 @@ makeSMSPage( GtkConnsState* state, PageData* data )
|
||||||
GtkWidget* vbox = boxWithUseCheck( state, data );
|
GtkWidget* vbox = boxWithUseCheck( state, data );
|
||||||
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
||||||
const gchar* phone = hasSMS ?
|
const gchar* phone = hasSMS ?
|
||||||
state->addr->u.sms.phone : state->globals->cGlobals.params->connInfo.sms.phone;
|
state->addr->u.sms.phone : state->globals->cGlobals.params->connInfo.sms.myPhone;
|
||||||
GtkWidget* hbox = makeLabeledField( "My phone", &state->smsphone, phone );
|
GtkWidget* hbox = makeLabeledField( "My phone", &state->smsphone, phone );
|
||||||
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
||||||
gtk_widget_set_sensitive( state->smsphone, !state->readOnly );
|
gtk_widget_set_sensitive( state->smsphone, !state->readOnly );
|
||||||
|
|
|
@ -237,7 +237,7 @@ makeSMSPage( GtkInviteState* state, PageData* data )
|
||||||
GtkWidget* vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
GtkWidget* vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
|
||||||
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
||||||
const gchar* phone = hasSMS ?
|
const gchar* phone = hasSMS ?
|
||||||
state->addr->u.sms.phone : state->globals->cGlobals.params->connInfo.sms.phone;
|
state->addr->u.sms.phone : state->globals->cGlobals.params->connInfo.sms.myPhone;
|
||||||
GtkWidget* hbox = makeLabeledField( "Invitee phone", &state->smsphone, phone );
|
GtkWidget* hbox = makeLabeledField( "Invitee phone", &state->smsphone, phone );
|
||||||
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,13 @@
|
||||||
#ifdef PLATFORM_GTK
|
#ifdef PLATFORM_GTK
|
||||||
|
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
#include "smsproto.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "gtkmain.h"
|
#include "gtkmain.h"
|
||||||
#include "gamesdb.h"
|
#include "gamesdb.h"
|
||||||
#include "gtkboard.h"
|
#include "gtkboard.h"
|
||||||
#include "linuxmain.h"
|
#include "linuxmain.h"
|
||||||
|
#include "linuxutl.h"
|
||||||
#include "relaycon.h"
|
#include "relaycon.h"
|
||||||
#include "linuxsms.h"
|
#include "linuxsms.h"
|
||||||
#include "gtkask.h"
|
#include "gtkask.h"
|
||||||
|
@ -275,7 +277,6 @@ handle_newgame_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
||||||
freeGlobals( globals );
|
freeGlobals( globals );
|
||||||
} else {
|
} else {
|
||||||
GtkWidget* gameWindow = globals->window;
|
GtkWidget* gameWindow = globals->window;
|
||||||
globals->cGlobals.pDb = apg->params->pDb;
|
|
||||||
globals->cGlobals.selRow = -1;
|
globals->cGlobals.selRow = -1;
|
||||||
recordOpened( apg, globals );
|
recordOpened( apg, globals );
|
||||||
gtk_widget_show( gameWindow );
|
gtk_widget_show( gameWindow );
|
||||||
|
@ -293,7 +294,6 @@ open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
|
||||||
apg->params->needsNewGame = XP_FALSE;
|
apg->params->needsNewGame = XP_FALSE;
|
||||||
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
||||||
initGlobals( globals, apg->params, NULL );
|
initGlobals( globals, apg->params, NULL );
|
||||||
globals->cGlobals.pDb = apg->params->pDb;
|
|
||||||
globals->cGlobals.selRow = row;
|
globals->cGlobals.selRow = row;
|
||||||
recordOpened( apg, globals );
|
recordOpened( apg, globals );
|
||||||
gtk_widget_show( globals->window );
|
gtk_widget_show( globals->window );
|
||||||
|
@ -315,10 +315,10 @@ handle_open_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
||||||
void
|
void
|
||||||
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
||||||
{
|
{
|
||||||
// LaunchParams* params = apg->params;
|
LaunchParams* params = apg->params;
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
XP_ASSERT( params == cGlobals->params );
|
||||||
cGlobals->params->vtMgr,
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
NULL, CHANNEL_NONE, NULL );
|
params->vtMgr );
|
||||||
|
|
||||||
/* Create new game. But has no addressing info, so need to set that
|
/* Create new game. But has no addressing info, so need to set that
|
||||||
aside for later. */
|
aside for later. */
|
||||||
|
@ -333,16 +333,15 @@ make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
||||||
game_saveNewGame( MPPARM(cGlobals->util->mpool) &gi,
|
game_saveNewGame( MPPARM(cGlobals->util->mpool) &gi,
|
||||||
cGlobals->util, &cGlobals->cp, stream );
|
cGlobals->util, &cGlobals->cp, stream );
|
||||||
|
|
||||||
sqlite3_int64 rowID = writeNewGameToDB( stream, cGlobals->pDb );
|
sqlite3_int64 rowID = writeNewGameToDB( stream, params->pDb );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
gi_disposePlayerInfo( MPPARM(cGlobals->util->mpool) &gi );
|
gi_disposePlayerInfo( MPPARM(cGlobals->util->mpool) &gi );
|
||||||
|
|
||||||
/* If it's a multi-device game, save enough information with it than when
|
/* If it's a multi-device game, save enough information with it than when
|
||||||
opened it can invite the other device[s] join the rematch. */
|
opened it can invite the other device[s] join the rematch. */
|
||||||
if ( !!comms ) {
|
if ( !!comms ) {
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
params->vtMgr );
|
||||||
NULL, CHANNEL_NONE, NULL );
|
|
||||||
CommsAddrRec addr;
|
CommsAddrRec addr;
|
||||||
comms_getAddr( comms, &addr );
|
comms_getAddr( comms, &addr );
|
||||||
addrToStream( stream, &addr );
|
addrToStream( stream, &addr );
|
||||||
|
@ -364,7 +363,7 @@ make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
||||||
}
|
}
|
||||||
addrToStream( stream, &addrs[ii] );
|
addrToStream( stream, &addrs[ii] );
|
||||||
}
|
}
|
||||||
saveInviteAddrs( stream, cGlobals->pDb, rowID );
|
saveInviteAddrs( stream, params->pDb, rowID );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +405,7 @@ handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
||||||
deleteGame( params->pDb, rowid );
|
deleteGame( params->pDb, rowid );
|
||||||
|
|
||||||
XP_UCHAR devIDBuf[64] = {0};
|
XP_UCHAR devIDBuf[64] = {0};
|
||||||
db_fetch( params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
db_fetch_safe( params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
||||||
if ( '\0' != devIDBuf[0] ) {
|
if ( '\0' != devIDBuf[0] ) {
|
||||||
relaycon_deleted( params, devIDBuf, clientToken );
|
relaycon_deleted( params, devIDBuf, clientToken );
|
||||||
} else {
|
} else {
|
||||||
|
@ -482,7 +481,7 @@ setWindowTitle( GtkAppGlobals* apg )
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
int len = strlen( title );
|
int len = strlen( title );
|
||||||
snprintf( &title[len], VSIZE(title) - len, " (phone: %s, port: %d)",
|
snprintf( &title[len], VSIZE(title) - len, " (phone: %s, port: %d)",
|
||||||
params->connInfo.sms.phone, params->connInfo.sms.port );
|
params->connInfo.sms.myPhone, params->connInfo.sms.port );
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_RELAY
|
#ifdef XWFEATURE_RELAY
|
||||||
XP_U32 relayID = linux_getDevIDRelay( params );
|
XP_U32 relayID = linux_getDevIDRelay( params );
|
||||||
|
@ -501,7 +500,7 @@ trySetWinConfig( GtkAppGlobals* apg )
|
||||||
int height = 400;
|
int height = 400;
|
||||||
|
|
||||||
gchar buf[64];
|
gchar buf[64];
|
||||||
if ( db_fetch( apg->params->pDb, KEY_WIN_LOC, buf, sizeof(buf)) ) {
|
if ( db_fetch_safe( apg->params->pDb, KEY_WIN_LOC, buf, sizeof(buf)) ) {
|
||||||
sscanf( buf, "%d:%d:%d:%d", &xx, &yy, &width, &height );
|
sscanf( buf, "%d:%d:%d:%d", &xx, &yy, &width, &height );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,7 +683,6 @@ relayInviteReceived( void* closure, NetLaunchInfo* invite )
|
||||||
// globals->cGlobals.addr = *returnAddr;
|
// globals->cGlobals.addr = *returnAddr;
|
||||||
|
|
||||||
GtkWidget* gameWindow = globals->window;
|
GtkWidget* gameWindow = globals->window;
|
||||||
globals->cGlobals.pDb = apg->params->pDb;
|
|
||||||
globals->cGlobals.selRow = -1;
|
globals->cGlobals.selRow = -1;
|
||||||
recordOpened( apg, globals );
|
recordOpened( apg, globals );
|
||||||
gtk_widget_show( gameWindow );
|
gtk_widget_show( gameWindow );
|
||||||
|
@ -730,7 +728,7 @@ requestMsgs( gpointer data )
|
||||||
{
|
{
|
||||||
GtkAppGlobals* apg = (GtkAppGlobals*)data;
|
GtkAppGlobals* apg = (GtkAppGlobals*)data;
|
||||||
XP_UCHAR devIDBuf[64] = {0};
|
XP_UCHAR devIDBuf[64] = {0};
|
||||||
db_fetch( apg->params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
db_fetch_safe( apg->params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
||||||
if ( '\0' != devIDBuf[0] ) {
|
if ( '\0' != devIDBuf[0] ) {
|
||||||
relaycon_requestMsgs( apg->params, devIDBuf );
|
relaycon_requestMsgs( apg->params, devIDBuf );
|
||||||
} else {
|
} else {
|
||||||
|
@ -755,8 +753,9 @@ smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName),
|
||||||
{
|
{
|
||||||
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
|
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
|
||||||
LaunchParams* params = apg->params;
|
LaunchParams* params = apg->params;
|
||||||
XP_LOGF( "%s(gameName=%s, gameID=%d, dictName=%s, nPlayers=%d, nHere=%d)",
|
XP_LOGF( "%s(gameName=%s, gameID=%d, dictName=%s, nPlayers=%d, "
|
||||||
__func__, gameName, gameID, dictName, nPlayers, nHere );
|
"nHere=%d, forceChannel=%d)", __func__, gameName, gameID, dictName,
|
||||||
|
nPlayers, nHere, forceChannel );
|
||||||
|
|
||||||
CurGameInfo gi = {0};
|
CurGameInfo gi = {0};
|
||||||
gi_copy( MPPARM(params->mpool) &gi, ¶ms->pgi );
|
gi_copy( MPPARM(params->mpool) &gi, ¶ms->pgi );
|
||||||
|
@ -765,6 +764,7 @@ smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName),
|
||||||
gi.gameID = gameID;
|
gi.gameID = gameID;
|
||||||
gi.dictLang = dictLang;
|
gi.dictLang = dictLang;
|
||||||
gi.forceChannel = forceChannel;
|
gi.forceChannel = forceChannel;
|
||||||
|
gi.serverRole = SERVER_ISCLIENT; /* recipient of invitation is client */
|
||||||
replaceStringIfDifferent( params->mpool, &gi.dictName, dictName );
|
replaceStringIfDifferent( params->mpool, &gi.dictName, dictName );
|
||||||
|
|
||||||
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
||||||
|
@ -773,7 +773,6 @@ smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName),
|
||||||
globals->cGlobals.addr = *returnAddr;
|
globals->cGlobals.addr = *returnAddr;
|
||||||
|
|
||||||
GtkWidget* gameWindow = globals->window;
|
GtkWidget* gameWindow = globals->window;
|
||||||
globals->cGlobals.pDb = apg->params->pDb;
|
|
||||||
globals->cGlobals.selRow = -1;
|
globals->cGlobals.selRow = -1;
|
||||||
recordOpened( apg, globals );
|
recordOpened( apg, globals );
|
||||||
gtk_widget_show( gameWindow );
|
gtk_widget_show( gameWindow );
|
||||||
|
@ -896,18 +895,18 @@ gtkmain( LaunchParams* params )
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
gchar buf[32];
|
gchar buf[32];
|
||||||
const gchar* myPhone = params->connInfo.sms.phone;
|
const gchar* myPhone = params->connInfo.sms.myPhone;
|
||||||
if ( !!myPhone ) {
|
if ( !!myPhone ) {
|
||||||
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
||||||
} else if ( !myPhone && db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
} else if ( !myPhone && db_fetch_safe( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||||
params->connInfo.sms.phone = myPhone = buf;
|
params->connInfo.sms.myPhone = myPhone = buf;
|
||||||
}
|
}
|
||||||
XP_U16 myPort = params->connInfo.sms.port;
|
XP_U16 myPort = params->connInfo.sms.port;
|
||||||
gchar portbuf[8];
|
gchar portbuf[8];
|
||||||
if ( 0 < myPort ) {
|
if ( 0 < myPort ) {
|
||||||
sprintf( portbuf, "%d", myPort );
|
sprintf( portbuf, "%d", myPort );
|
||||||
db_store( params->pDb, KEY_SMSPORT, portbuf );
|
db_store( params->pDb, KEY_SMSPORT, portbuf );
|
||||||
} else if ( db_fetch( params->pDb, KEY_SMSPORT, portbuf, VSIZE(portbuf) ) ) {
|
} else if ( db_fetch_safe( params->pDb, KEY_SMSPORT, portbuf, VSIZE(portbuf) ) ) {
|
||||||
params->connInfo.sms.port = myPort = atoi( portbuf );
|
params->connInfo.sms.port = myPort = atoi( portbuf );
|
||||||
}
|
}
|
||||||
if ( !!myPhone && 0 < myPort ) {
|
if ( !!myPhone && 0 < myPort ) {
|
||||||
|
@ -921,7 +920,13 @@ gtkmain( LaunchParams* params )
|
||||||
XP_LOGF( "not activating SMS: I don't have a phone" );
|
XP_LOGF( "not activating SMS: I don't have a phone" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( params->runSMSTest ) {
|
||||||
|
CommonGlobals cGlobals = {.params = params };
|
||||||
|
setupUtil( &cGlobals );
|
||||||
|
smsproto_runTests( params->mpool, cGlobals.params->dutil );
|
||||||
|
linux_util_vt_destroy( cGlobals.util );
|
||||||
|
free( cGlobals.util );
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
makeGamesWindow( &apg );
|
makeGamesWindow( &apg );
|
||||||
} else if ( !!params->dbFileName ) {
|
} else if ( !!params->dbFileName ) {
|
||||||
|
|
315
xwords4/linux/lindutil.c
Normal file
315
xwords4/linux/lindutil.c
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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 "dutil.h"
|
||||||
|
#include "mempool.h"
|
||||||
|
#include "lindutil.h"
|
||||||
|
#include "linuxutl.h"
|
||||||
|
#include "linuxmain.h"
|
||||||
|
#include "gamesdb.h"
|
||||||
|
#include "LocalizedStrIncludes.h"
|
||||||
|
|
||||||
|
static XP_U32 linux_dutil_getCurSeconds( XW_DUtilCtxt* duc );
|
||||||
|
static const XP_UCHAR* linux_dutil_getUserString( XW_DUtilCtxt* duc, XP_U16 code );
|
||||||
|
static const XP_UCHAR* linux_dutil_getUserQuantityString( XW_DUtilCtxt* duc, XP_U16 code,
|
||||||
|
XP_U16 quantity );
|
||||||
|
|
||||||
|
static void linux_dutil_storeStream( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* data );
|
||||||
|
static void linux_dutil_loadStream( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* inOut );
|
||||||
|
static void linux_dutil_storePtr( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
const void* data, XP_U16 len );
|
||||||
|
static void linux_dutil_loadPtr( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
void* data, XP_U16* lenp );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
static XP_Bool linux_dutil_phoneNumbersSame( XW_DUtilCtxt* duc,
|
||||||
|
const XP_UCHAR* p1,
|
||||||
|
const XP_UCHAR* p2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
static const XP_UCHAR* linux_dutil_getDevID( XW_DUtilCtxt* duc, DevIDType* typ );
|
||||||
|
static void linux_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
|
||||||
|
const XP_UCHAR* idRelay );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
static XP_UCHAR* linux_dutil_md5sum( XW_DUtilCtxt* duc, const XP_U8* ptr,
|
||||||
|
XP_U16 len );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XW_DUtilCtxt*
|
||||||
|
dutils_init( MPFORMAL VTableMgr* vtMgr, void* closure )
|
||||||
|
{
|
||||||
|
XW_DUtilCtxt* result = XP_CALLOC( mpool, sizeof(*result) );
|
||||||
|
result->vtMgr = vtMgr;
|
||||||
|
result->closure = closure;
|
||||||
|
|
||||||
|
# define SET_PROC(nam) \
|
||||||
|
result->vtable.m_dutil_ ## nam = linux_dutil_ ## nam;
|
||||||
|
|
||||||
|
SET_PROC(getCurSeconds);
|
||||||
|
SET_PROC(getUserString);
|
||||||
|
SET_PROC(getUserQuantityString);
|
||||||
|
SET_PROC(storeStream);
|
||||||
|
SET_PROC(loadStream);
|
||||||
|
SET_PROC(storePtr);
|
||||||
|
SET_PROC(loadPtr);
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
SET_PROC(phoneNumbersSame);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
SET_PROC(getDevID);
|
||||||
|
SET_PROC(deviceRegistered);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
SET_PROC(md5sum);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
# undef SET_PROC
|
||||||
|
|
||||||
|
MPASSIGN( result->mpool, mpool );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dutils_free( XW_DUtilCtxt** ducp )
|
||||||
|
{
|
||||||
|
# ifdef MEM_DEBUG
|
||||||
|
XP_FREEP( (*ducp)->mpool, ducp );
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static XP_U32
|
||||||
|
linux_dutil_getCurSeconds( XW_DUtilCtxt* XP_UNUSED(duc) )
|
||||||
|
{
|
||||||
|
return linux_getCurSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const XP_UCHAR*
|
||||||
|
linux_dutil_getUserString( XW_DUtilCtxt* XP_UNUSED(uc), XP_U16 code )
|
||||||
|
{
|
||||||
|
switch( code ) {
|
||||||
|
case STRD_REMAINING_TILES_ADD:
|
||||||
|
return (XP_UCHAR*)"+ %d [all remaining tiles]";
|
||||||
|
case STRD_UNUSED_TILES_SUB:
|
||||||
|
return (XP_UCHAR*)"- %d [unused tiles]";
|
||||||
|
case STR_COMMIT_CONFIRM:
|
||||||
|
return (XP_UCHAR*)"Are you sure you want to commit the current move?\n";
|
||||||
|
case STRD_TURN_SCORE:
|
||||||
|
return (XP_UCHAR*)"Score for turn: %d\n";
|
||||||
|
case STR_BONUS_ALL:
|
||||||
|
return (XP_UCHAR*)"Bonus for using all tiles: 50\n";
|
||||||
|
case STR_LOCAL_NAME:
|
||||||
|
return (XP_UCHAR*)"%s";
|
||||||
|
case STR_NONLOCAL_NAME:
|
||||||
|
return (XP_UCHAR*)"%s (remote)";
|
||||||
|
case STRD_TIME_PENALTY_SUB:
|
||||||
|
return (XP_UCHAR*)" - %d [time]";
|
||||||
|
/* added.... */
|
||||||
|
case STRD_CUMULATIVE_SCORE:
|
||||||
|
return (XP_UCHAR*)"Cumulative score: %d\n";
|
||||||
|
case STRS_TRAY_AT_START:
|
||||||
|
return (XP_UCHAR*)"Tray at start: %s\n";
|
||||||
|
case STRS_MOVE_DOWN:
|
||||||
|
return (XP_UCHAR*)"move (from %s down)\n";
|
||||||
|
case STRS_MOVE_ACROSS:
|
||||||
|
return (XP_UCHAR*)"move (from %s across)\n";
|
||||||
|
case STRS_NEW_TILES:
|
||||||
|
return (XP_UCHAR*)"New tiles: %s\n";
|
||||||
|
case STRSS_TRADED_FOR:
|
||||||
|
return (XP_UCHAR*)"Traded %s for %s.";
|
||||||
|
case STR_PASS:
|
||||||
|
return (XP_UCHAR*)"pass\n";
|
||||||
|
case STR_PHONY_REJECTED:
|
||||||
|
return (XP_UCHAR*)"Illegal word in move; turn lost!\n";
|
||||||
|
|
||||||
|
case STRD_ROBOT_TRADED:
|
||||||
|
return (XP_UCHAR*)"%d tiles traded this turn.";
|
||||||
|
case STR_ROBOT_MOVED:
|
||||||
|
return (XP_UCHAR*)"The robot \"%s\" moved:\n";
|
||||||
|
case STRS_REMOTE_MOVED:
|
||||||
|
return (XP_UCHAR*)"Remote player \"%s\" moved:\n";
|
||||||
|
|
||||||
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
|
case STR_LOCALPLAYERS:
|
||||||
|
return (XP_UCHAR*)"Local players";
|
||||||
|
case STR_REMOTE:
|
||||||
|
return (XP_UCHAR*)"Remote";
|
||||||
|
#endif
|
||||||
|
case STR_TOTALPLAYERS:
|
||||||
|
return (XP_UCHAR*)"Total players";
|
||||||
|
|
||||||
|
case STRS_VALUES_HEADER:
|
||||||
|
return (XP_UCHAR*)"%s counts/values:\n";
|
||||||
|
|
||||||
|
case STRD_REMAINS_HEADER:
|
||||||
|
return (XP_UCHAR*)"%d tiles left in pool.";
|
||||||
|
case STRD_REMAINS_EXPL:
|
||||||
|
return (XP_UCHAR*)"%d tiles left in pool and hidden trays:\n";
|
||||||
|
|
||||||
|
case STRSD_RESIGNED:
|
||||||
|
return "[Resigned] %s: %d";
|
||||||
|
case STRSD_WINNER:
|
||||||
|
return "[Winner] %s: %d";
|
||||||
|
case STRDSD_PLACER:
|
||||||
|
return "[#%d] %s: %d";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (XP_UCHAR*)"unknown code to linux_util_getUserString";
|
||||||
|
}
|
||||||
|
} /* linux_dutil_getUserString */
|
||||||
|
|
||||||
|
static const XP_UCHAR*
|
||||||
|
linux_dutil_getUserQuantityString( XW_DUtilCtxt* duc, XP_U16 code,
|
||||||
|
XP_U16 XP_UNUSED(quantity) )
|
||||||
|
{
|
||||||
|
return linux_dutil_getUserString( duc, code );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_dutil_storeStream( XW_DUtilCtxt* duc, const XP_UCHAR* key, XWStreamCtxt* stream )
|
||||||
|
{
|
||||||
|
const void* ptr = stream_getPtr( stream );
|
||||||
|
XP_U16 len = stream_getSize( stream );
|
||||||
|
linux_dutil_storePtr( duc, key, ptr, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_dutil_loadStream( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
XWStreamCtxt* stream )
|
||||||
|
{
|
||||||
|
XP_U16 len = 0;
|
||||||
|
linux_dutil_loadPtr( duc, key, NULL, &len );
|
||||||
|
XP_U8 buf[len];
|
||||||
|
linux_dutil_loadPtr( duc, key, buf, &len );
|
||||||
|
|
||||||
|
gsize out_len;
|
||||||
|
guchar* txt = g_base64_decode( (const gchar*)buf, &out_len );
|
||||||
|
stream_putBytes( stream, txt, out_len );
|
||||||
|
g_free( txt );
|
||||||
|
|
||||||
|
XP_LOGF( "%s(key=%s) => len: %d", __func__, key, stream_getSize(stream) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_dutil_storePtr( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
const void* data, XP_U16 len )
|
||||||
|
{
|
||||||
|
LaunchParams* params = (LaunchParams*)duc->closure;
|
||||||
|
sqlite3* pDb = params->pDb;
|
||||||
|
|
||||||
|
gchar* b64 = g_base64_encode( data, len);
|
||||||
|
db_store( pDb, key, b64 );
|
||||||
|
g_free( b64 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_dutil_loadPtr( XW_DUtilCtxt* duc, const XP_UCHAR* key,
|
||||||
|
void* data, XP_U16* lenp )
|
||||||
|
{
|
||||||
|
LaunchParams* params = (LaunchParams*)duc->closure;
|
||||||
|
sqlite3* pDb = params->pDb;
|
||||||
|
|
||||||
|
gint buflen = 0;
|
||||||
|
FetchResult res = db_fetch( pDb, key, NULL, &buflen );
|
||||||
|
if ( res == BUFFER_TOO_SMALL ) { /* expected: I passed 0 */
|
||||||
|
void* tmp = XP_MALLOC( duc->mpool, buflen );
|
||||||
|
res = db_fetch( pDb, key, tmp, &buflen );
|
||||||
|
XP_ASSERT( res == SUCCESS );
|
||||||
|
|
||||||
|
gsize out_len;
|
||||||
|
guchar* txt = g_base64_decode( (const gchar*)tmp, &out_len );
|
||||||
|
if ( out_len <= *lenp ) {
|
||||||
|
XP_MEMCPY( data, txt, out_len );
|
||||||
|
*lenp = out_len;
|
||||||
|
}
|
||||||
|
XP_FREEP( duc->mpool, &tmp );
|
||||||
|
g_free( txt );
|
||||||
|
} else {
|
||||||
|
*lenp = 0; /* doesn't exist */
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_LOGF( "%s(key=%s) => len: %d", __func__, key, *lenp );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_SMS
|
||||||
|
static XP_Bool
|
||||||
|
linux_dutil_phoneNumbersSame( XW_DUtilCtxt* duc, const XP_UCHAR* p1,
|
||||||
|
const XP_UCHAR* p2 )
|
||||||
|
{
|
||||||
|
LOG_FUNC();
|
||||||
|
XP_USE( duc );
|
||||||
|
XP_Bool result = 0 == strcmp( p1, p2 );
|
||||||
|
XP_LOGF( "%s(%s, %s) => %d", __func__, p1, p2, result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
static const XP_UCHAR*
|
||||||
|
linux_dutil_getDevID( XW_DUtilCtxt* duc, DevIDType* typ )
|
||||||
|
{
|
||||||
|
LaunchParams* params = (LaunchParams*)duc->closure;
|
||||||
|
return linux_getDevID( params, typ );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
|
||||||
|
const XP_UCHAR* idRelay )
|
||||||
|
{
|
||||||
|
/* Script discon_ok2.sh is grepping for these strings in logs, so don't
|
||||||
|
change them! */
|
||||||
|
LaunchParams* params = (LaunchParams*)duc->closure;
|
||||||
|
switch( typ ) {
|
||||||
|
case ID_TYPE_NONE: /* error case */
|
||||||
|
XP_LOGF( "%s: id rejected", __func__ );
|
||||||
|
params->lDevID = NULL;
|
||||||
|
break;
|
||||||
|
case ID_TYPE_RELAY:
|
||||||
|
if ( !!params->pDb && 0 < strlen( idRelay ) ) {
|
||||||
|
XP_LOGF( "%s: new id: %s", __func__, idRelay );
|
||||||
|
db_store( params->pDb, KEY_RDEVID, idRelay );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
XP_ASSERT(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
static XP_UCHAR*
|
||||||
|
linux_dutil_md5sum( XW_DUtilCtxt* duc, const XP_U8* ptr, XP_U16 len )
|
||||||
|
{
|
||||||
|
gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, len );
|
||||||
|
XP_U16 sumlen = 1 + strlen( sum );
|
||||||
|
XP_UCHAR* result = XP_MALLOC( duc->mpool, sumlen );
|
||||||
|
XP_MEMCPY( result, sum, sumlen );
|
||||||
|
g_free( sum );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
28
xwords4/linux/lindutil.h
Normal file
28
xwords4/linux/lindutil.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LINDUTIL_H_
|
||||||
|
#define _LINDUTIL_H_
|
||||||
|
|
||||||
|
#include "dutil.h"
|
||||||
|
|
||||||
|
XW_DUtilCtxt* dutils_init( MPFORMAL VTableMgr* vtMgr, void* closure );
|
||||||
|
void dutils_free( XW_DUtilCtxt** ducp );
|
||||||
|
|
||||||
|
#endif
|
|
@ -57,7 +57,9 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "gamesdb.h"
|
#include "gamesdb.h"
|
||||||
#include "linuxdict.h"
|
#include "linuxdict.h"
|
||||||
|
#include "lindutil.h"
|
||||||
#include "relaycon.h"
|
#include "relaycon.h"
|
||||||
|
#include "smsproto.h"
|
||||||
#ifdef PLATFORM_NCURSES
|
#ifdef PLATFORM_NCURSES
|
||||||
# include "cursesmain.h"
|
# include "cursesmain.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -95,7 +97,7 @@ file_exists( const char* fileName )
|
||||||
} /* file_exists */
|
} /* file_exists */
|
||||||
|
|
||||||
XWStreamCtxt*
|
XWStreamCtxt*
|
||||||
streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
streamFromFile( CommonGlobals* cGlobals, char* name )
|
||||||
{
|
{
|
||||||
XP_U8* buf;
|
XP_U8* buf;
|
||||||
struct stat statBuf;
|
struct stat statBuf;
|
||||||
|
@ -110,9 +112,8 @@ streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
||||||
}
|
}
|
||||||
fclose( f );
|
fclose( f );
|
||||||
|
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
cGlobals->params->vtMgr );
|
||||||
closure, CHANNEL_NONE, NULL );
|
|
||||||
stream_putBytes( stream, buf, statBuf.st_size );
|
stream_putBytes( stream, buf, statBuf.st_size );
|
||||||
free( buf );
|
free( buf );
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
||||||
|
|
||||||
#ifdef USE_SQLITE
|
#ifdef USE_SQLITE
|
||||||
XWStreamCtxt*
|
XWStreamCtxt*
|
||||||
streamFromDB( CommonGlobals* cGlobals, void* closure )
|
streamFromDB( CommonGlobals* cGlobals )
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
XWStreamCtxt* stream = NULL;
|
XWStreamCtxt* stream = NULL;
|
||||||
|
@ -139,9 +140,8 @@ streamFromDB( CommonGlobals* cGlobals, void* closure )
|
||||||
XP_U8 buf[size];
|
XP_U8 buf[size];
|
||||||
res = sqlite3_blob_read( ppBlob, buf, size, 0 );
|
res = sqlite3_blob_read( ppBlob, buf, size, 0 );
|
||||||
if ( SQLITE_OK == res ) {
|
if ( SQLITE_OK == res ) {
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
params->vtMgr,
|
params->vtMgr );
|
||||||
closure, CHANNEL_NONE, NULL );
|
|
||||||
stream_putBytes( stream, buf, size );
|
stream_putBytes( stream, buf, size );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ requestMsgsIdle( gpointer data )
|
||||||
{
|
{
|
||||||
CommonGlobals* cGlobals = (CommonGlobals*)data;
|
CommonGlobals* cGlobals = (CommonGlobals*)data;
|
||||||
XP_UCHAR devIDBuf[64] = {0};
|
XP_UCHAR devIDBuf[64] = {0};
|
||||||
db_fetch( cGlobals->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
db_fetch_safe( cGlobals->params->pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
|
||||||
if ( '\0' != devIDBuf[0] ) {
|
if ( '\0' != devIDBuf[0] ) {
|
||||||
relaycon_requestMsgs( cGlobals->params, devIDBuf );
|
relaycon_requestMsgs( cGlobals->params, devIDBuf );
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,8 +352,9 @@ void
|
||||||
saveGame( CommonGlobals* cGlobals )
|
saveGame( CommonGlobals* cGlobals )
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
|
sqlite3* pDb = cGlobals->params->pDb;
|
||||||
if ( !!cGlobals->game.model &&
|
if ( !!cGlobals->game.model &&
|
||||||
(!!cGlobals->params->fileName || !!cGlobals->pDb) ) {
|
(!!cGlobals->params->fileName || !!pDb) ) {
|
||||||
XP_Bool doSave = XP_TRUE;
|
XP_Bool doSave = XP_TRUE;
|
||||||
XP_Bool newGame = !file_exists( cGlobals->params->fileName )
|
XP_Bool newGame = !file_exists( cGlobals->params->fileName )
|
||||||
|| -1 == cGlobals->selRow;
|
|| -1 == cGlobals->selRow;
|
||||||
|
@ -364,13 +365,12 @@ saveGame( CommonGlobals* cGlobals )
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( doSave ) {
|
if ( doSave ) {
|
||||||
if ( !!cGlobals->pDb ) {
|
if ( !!pDb ) {
|
||||||
summarize( cGlobals );
|
summarize( cGlobals );
|
||||||
}
|
}
|
||||||
|
|
||||||
XWStreamCtxt* outStream;
|
XWStreamCtxt* outStream;
|
||||||
MemStreamCloseCallback onClose = !!cGlobals->pDb?
|
MemStreamCloseCallback onClose = !!pDb? writeToDB : writeToFile;
|
||||||
writeToDB : writeToFile;
|
|
||||||
outStream =
|
outStream =
|
||||||
mem_stream_make_sized( MPPARM(cGlobals->util->mpool)
|
mem_stream_make_sized( MPPARM(cGlobals->util->mpool)
|
||||||
cGlobals->params->vtMgr,
|
cGlobals->params->vtMgr,
|
||||||
|
@ -398,8 +398,7 @@ handle_messages_from( CommonGlobals* cGlobals, const TransportProcs* procs,
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
LaunchParams* params = cGlobals->params;
|
LaunchParams* params = cGlobals->params;
|
||||||
XWStreamCtxt* stream =
|
XWStreamCtxt* stream = streamFromFile( cGlobals, params->fileName );
|
||||||
streamFromFile( cGlobals, params->fileName, cGlobals );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
XP_Bool opened =
|
XP_Bool opened =
|
||||||
|
@ -431,9 +430,8 @@ handle_messages_from( CommonGlobals* cGlobals, const TransportProcs* procs,
|
||||||
XP_LOGF( "%s: 2: unexpected nRead: %zd", __func__, nRead );
|
XP_LOGF( "%s: 2: unexpected nRead: %zd", __func__, nRead );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
params->vtMgr, cGlobals, CHANNEL_NONE,
|
params->vtMgr );
|
||||||
NULL );
|
|
||||||
stream_putBytes( stream, buf, len );
|
stream_putBytes( stream, buf, len );
|
||||||
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
@ -447,8 +445,7 @@ read_pipe_then_close( CommonGlobals* cGlobals, const TransportProcs* procs )
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
LaunchParams* params = cGlobals->params;
|
LaunchParams* params = cGlobals->params;
|
||||||
XWStreamCtxt* stream =
|
XWStreamCtxt* stream = streamFromFile( cGlobals, params->fileName );
|
||||||
streamFromFile( cGlobals, params->fileName, cGlobals );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
XP_Bool opened =
|
XP_Bool opened =
|
||||||
|
@ -483,9 +480,8 @@ read_pipe_then_close( CommonGlobals* cGlobals, const TransportProcs* procs )
|
||||||
XP_LOGF( "%s: 2: unexpected nRead: %zd", __func__, nRead );
|
XP_LOGF( "%s: 2: unexpected nRead: %zd", __func__, nRead );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
params->vtMgr, cGlobals, CHANNEL_NONE,
|
params->vtMgr );
|
||||||
NULL );
|
|
||||||
stream_putBytes( stream, buf, len );
|
stream_putBytes( stream, buf, len );
|
||||||
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
@ -643,6 +639,7 @@ typedef enum {
|
||||||
,CMD_DROPSENDRELAY
|
,CMD_DROPSENDRELAY
|
||||||
,CMD_DROPRCVRELAY
|
,CMD_DROPRCVRELAY
|
||||||
,CMD_DROPSENDSMS
|
,CMD_DROPSENDSMS
|
||||||
|
,CMD_SMSFAILPCT
|
||||||
,CMD_DROPRCVSMS
|
,CMD_DROPRCVSMS
|
||||||
,CMD_FORCECHANNEL
|
,CMD_FORCECHANNEL
|
||||||
|
|
||||||
|
@ -656,6 +653,7 @@ typedef enum {
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
,CMD_SMSNUMBER /* SMS phone number */
|
,CMD_SMSNUMBER /* SMS phone number */
|
||||||
|
,CMD_SERVER_SMSNUMBER
|
||||||
,CMD_SMSPORT
|
,CMD_SMSPORT
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_RELAY
|
#ifdef XWFEATURE_RELAY
|
||||||
|
@ -684,6 +682,7 @@ typedef enum {
|
||||||
,CMD_NHIDDENROWS
|
,CMD_NHIDDENROWS
|
||||||
#endif
|
#endif
|
||||||
,CMD_ASKTIME
|
,CMD_ASKTIME
|
||||||
|
,CMD_SMSTEST
|
||||||
,N_CMDS
|
,N_CMDS
|
||||||
} XwLinuxCmd;
|
} XwLinuxCmd;
|
||||||
|
|
||||||
|
@ -764,6 +763,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
||||||
,{ CMD_DROPSENDRELAY, false, "drop-send-relay", "start new games with relay send disabled" }
|
,{ CMD_DROPSENDRELAY, false, "drop-send-relay", "start new games with relay send disabled" }
|
||||||
,{ CMD_DROPRCVRELAY, false, "drop-receive-relay", "start new games with relay receive disabled" }
|
,{ CMD_DROPRCVRELAY, false, "drop-receive-relay", "start new games with relay receive disabled" }
|
||||||
,{ CMD_DROPSENDSMS, false, "drop-send-sms", "start new games with sms send disabled" }
|
,{ CMD_DROPSENDSMS, false, "drop-send-sms", "start new games with sms send disabled" }
|
||||||
|
,{ CMD_SMSFAILPCT, true, "sms-fail-pct", "percent of sms sends, randomly chosen, never arrive" }
|
||||||
,{ CMD_DROPRCVSMS, false, "drop-receive-sms", "start new games with sms receive disabled" }
|
,{ CMD_DROPRCVSMS, false, "drop-receive-sms", "start new games with sms receive disabled" }
|
||||||
,{ CMD_FORCECHANNEL, true, "force-channel", "force (clients) to use this hostid/channel" }
|
,{ CMD_FORCECHANNEL, true, "force-channel", "force (clients) to use this hostid/channel" }
|
||||||
|
|
||||||
|
@ -779,6 +779,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
,{ CMD_SMSNUMBER, true, "sms-number", "this devices's sms phone number" }
|
,{ CMD_SMSNUMBER, true, "sms-number", "this devices's sms phone number" }
|
||||||
|
,{ CMD_SERVER_SMSNUMBER, true, "server-sms-number", "number this device should connect to" }
|
||||||
,{ CMD_SMSPORT, true, "sms-port", "this devices's sms port" }
|
,{ CMD_SMSPORT, true, "sms-port", "this devices's sms port" }
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_RELAY
|
#ifdef XWFEATURE_RELAY
|
||||||
|
@ -811,6 +812,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
||||||
,{ CMD_ASKTIME, true, "ask-timeout",
|
,{ CMD_ASKTIME, true, "ask-timeout",
|
||||||
"Wait this many ms before cancelling dialog (default 500 ms; 0 means forever)" }
|
"Wait this many ms before cancelling dialog (default 500 ms; 0 means forever)" }
|
||||||
#endif
|
#endif
|
||||||
|
,{ CMD_SMSTEST, false, "run-sms-test", "Run smsproto_runTests() on startup"}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct option*
|
static struct option*
|
||||||
|
@ -910,7 +912,7 @@ linux_getDevIDRelay( LaunchParams* params )
|
||||||
{
|
{
|
||||||
XP_U32 result = 0;
|
XP_U32 result = 0;
|
||||||
gchar buf[32];
|
gchar buf[32];
|
||||||
if ( db_fetch( params->pDb, KEY_RDEVID, buf, sizeof(buf) ) ) {
|
if ( db_fetch_safe( params->pDb, KEY_RDEVID, buf, sizeof(buf) ) ) {
|
||||||
sscanf( buf, "%X", &result );
|
sscanf( buf, "%X", &result );
|
||||||
XP_LOGF( "%s(): %s => %x", __func__, buf, result );
|
XP_LOGF( "%s(): %s => %x", __func__, buf, result );
|
||||||
}
|
}
|
||||||
|
@ -918,6 +920,12 @@ linux_getDevIDRelay( LaunchParams* params )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XP_U32
|
||||||
|
linux_getCurSeconds()
|
||||||
|
{
|
||||||
|
return (XP_U32)time(NULL);//tv.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
const XP_UCHAR*
|
const XP_UCHAR*
|
||||||
linux_getDevID( LaunchParams* params, DevIDType* typ )
|
linux_getDevID( LaunchParams* params, DevIDType* typ )
|
||||||
{
|
{
|
||||||
|
@ -928,11 +936,11 @@ linux_getDevID( LaunchParams* params, DevIDType* typ )
|
||||||
if ( !!params->lDevID ) {
|
if ( !!params->lDevID ) {
|
||||||
result = params->lDevID;
|
result = params->lDevID;
|
||||||
*typ = ID_TYPE_LINUX;
|
*typ = ID_TYPE_LINUX;
|
||||||
} else if ( db_fetch( params->pDb, KEY_RDEVID, params->devIDStore,
|
} else if ( db_fetch_safe( params->pDb, KEY_RDEVID, params->devIDStore,
|
||||||
sizeof(params->devIDStore) ) ) {
|
sizeof(params->devIDStore) ) ) {
|
||||||
result = params->devIDStore;
|
result = params->devIDStore;
|
||||||
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_RELAY;
|
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_RELAY;
|
||||||
} else if ( db_fetch( params->pDb, KEY_LDEVID, params->devIDStore,
|
} else if ( db_fetch_safe( params->pDb, KEY_LDEVID, params->devIDStore,
|
||||||
sizeof(params->devIDStore) ) ) {
|
sizeof(params->devIDStore) ) ) {
|
||||||
result = params->devIDStore;
|
result = params->devIDStore;
|
||||||
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_LINUX;
|
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_LINUX;
|
||||||
|
@ -947,7 +955,7 @@ void
|
||||||
linux_doInitialReg( LaunchParams* params, XP_Bool idIsNew )
|
linux_doInitialReg( LaunchParams* params, XP_Bool idIsNew )
|
||||||
{
|
{
|
||||||
gchar rDevIDBuf[64];
|
gchar rDevIDBuf[64];
|
||||||
if ( !db_fetch( params->pDb, KEY_RDEVID, rDevIDBuf,
|
if ( !db_fetch_safe( params->pDb, KEY_RDEVID, rDevIDBuf,
|
||||||
sizeof(rDevIDBuf) ) ) {
|
sizeof(rDevIDBuf) ) ) {
|
||||||
rDevIDBuf[0] = '\0';
|
rDevIDBuf[0] = '\0';
|
||||||
}
|
}
|
||||||
|
@ -964,7 +972,7 @@ linux_setupDevidParams( LaunchParams* params )
|
||||||
{
|
{
|
||||||
XP_Bool idIsNew = XP_TRUE;
|
XP_Bool idIsNew = XP_TRUE;
|
||||||
gchar oldLDevID[256];
|
gchar oldLDevID[256];
|
||||||
if ( db_fetch( params->pDb, KEY_LDEVID, oldLDevID, sizeof(oldLDevID) )
|
if ( db_fetch_safe( params->pDb, KEY_LDEVID, oldLDevID, sizeof(oldLDevID) )
|
||||||
&& (!params->lDevID || 0 == strcmp( oldLDevID, params->lDevID )) ) {
|
&& (!params->lDevID || 0 == strcmp( oldLDevID, params->lDevID )) ) {
|
||||||
idIsNew = XP_FALSE;
|
idIsNew = XP_FALSE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1299,9 +1307,15 @@ linux_send( const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* XP_UNUSED_DBG(msgNo
|
||||||
comms_getAddr( cGlobals->game.comms, &addr );
|
comms_getAddr( cGlobals->game.comms, &addr );
|
||||||
addrRec = &addr;
|
addrRec = &addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use serverphone if I'm a client, else hope one's provided (this is
|
||||||
|
// a reply)
|
||||||
|
const XP_UCHAR* phone = cGlobals->params->connInfo.sms.serverPhone;
|
||||||
|
if ( !phone ) {
|
||||||
|
phone = addrRec->u.sms.phone;
|
||||||
|
}
|
||||||
nSent = linux_sms_send( cGlobals->params, buf, buflen,
|
nSent = linux_sms_send( cGlobals->params, buf, buflen,
|
||||||
addrRec->u.sms.phone, addrRec->u.sms.port,
|
phone, addrRec->u.sms.port, gameID );
|
||||||
gameID );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1434,9 +1448,8 @@ stream_from_msgbuf( CommonGlobals* globals, const unsigned char* bufPtr,
|
||||||
XP_U16 nBytes )
|
XP_U16 nBytes )
|
||||||
{
|
{
|
||||||
XWStreamCtxt* result;
|
XWStreamCtxt* result;
|
||||||
result = mem_stream_make( MPPARM(globals->util->mpool)
|
result = mem_stream_make_raw( MPPARM(globals->util->mpool)
|
||||||
globals->params->vtMgr,
|
globals->params->vtMgr );
|
||||||
globals, CHANNEL_NONE, NULL );
|
|
||||||
stream_putBytes( result, bufPtr, nBytes );
|
stream_putBytes( result, bufPtr, nBytes );
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1950,7 +1963,7 @@ initFromParams( CommonGlobals* cGlobals, LaunchParams* params )
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
case COMMS_CONN_SMS:
|
case COMMS_CONN_SMS:
|
||||||
addr_addType( addr, COMMS_CONN_SMS );
|
addr_addType( addr, COMMS_CONN_SMS );
|
||||||
XP_STRNCPY( addr->u.sms.phone, params->connInfo.sms.phone,
|
XP_STRNCPY( addr->u.sms.phone, params->connInfo.sms.myPhone,
|
||||||
sizeof(addr->u.sms.phone) - 1 );
|
sizeof(addr->u.sms.phone) - 1 );
|
||||||
addr->u.sms.port = params->connInfo.sms.port;
|
addr->u.sms.port = params->connInfo.sms.port;
|
||||||
break;
|
break;
|
||||||
|
@ -2001,12 +2014,15 @@ initParams( LaunchParams* params )
|
||||||
/* params->util->vtable->m_util_addrChange = linux_util_addrChange; */
|
/* params->util->vtable->m_util_addrChange = linux_util_addrChange; */
|
||||||
/* params->util->vtable->m_util_setIsServer = linux_util_setIsServer; */
|
/* params->util->vtable->m_util_setIsServer = linux_util_setIsServer; */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
params->dutil = dutils_init( MPPARM(params->mpool) params->vtMgr, params );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
freeParams( LaunchParams* params )
|
freeParams( LaunchParams* params )
|
||||||
{
|
{
|
||||||
vtmgr_destroy( MPPARM(params->mpool) params->vtMgr );
|
vtmgr_destroy( MPPARM(params->mpool) params->vtMgr );
|
||||||
|
dutils_free( ¶ms->dutil );
|
||||||
dmgr_destroy( params->dictMgr );
|
dmgr_destroy( params->dictMgr );
|
||||||
|
|
||||||
gi_disposePlayerInfo( MPPARM(params->mpool) ¶ms->pgi );
|
gi_disposePlayerInfo( MPPARM(params->mpool) ¶ms->pgi );
|
||||||
|
@ -2272,7 +2288,11 @@ main( int argc, char** argv )
|
||||||
break;
|
break;
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
case CMD_SMSNUMBER: /* SMS phone number */
|
case CMD_SMSNUMBER: /* SMS phone number */
|
||||||
mainParams.connInfo.sms.phone = optarg;
|
mainParams.connInfo.sms.myPhone = optarg;
|
||||||
|
addr_addType( &mainParams.addr, COMMS_CONN_SMS );
|
||||||
|
break;
|
||||||
|
case CMD_SERVER_SMSNUMBER:
|
||||||
|
mainParams.connInfo.sms.serverPhone = optarg;
|
||||||
addr_addType( &mainParams.addr, COMMS_CONN_SMS );
|
addr_addType( &mainParams.addr, COMMS_CONN_SMS );
|
||||||
break;
|
break;
|
||||||
case CMD_SMSPORT:
|
case CMD_SMSPORT:
|
||||||
|
@ -2427,6 +2447,10 @@ main( int argc, char** argv )
|
||||||
case CMD_DROPSENDSMS:
|
case CMD_DROPSENDSMS:
|
||||||
mainParams.commsDisableds[COMMS_CONN_SMS][1] = XP_TRUE;
|
mainParams.commsDisableds[COMMS_CONN_SMS][1] = XP_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case CMD_SMSFAILPCT:
|
||||||
|
mainParams.smsSendFailPct = atoi(optarg);
|
||||||
|
XP_ASSERT( mainParams.smsSendFailPct >= 0 && mainParams.smsSendFailPct <= 100 );
|
||||||
|
break;
|
||||||
case CMD_DROPRCVSMS:
|
case CMD_DROPRCVSMS:
|
||||||
mainParams.commsDisableds[COMMS_CONN_SMS][0] = XP_TRUE;
|
mainParams.commsDisableds[COMMS_CONN_SMS][0] = XP_TRUE;
|
||||||
break;
|
break;
|
||||||
|
@ -2488,6 +2512,10 @@ main( int argc, char** argv )
|
||||||
mainParams.askTimeout = atoi(optarg);
|
mainParams.askTimeout = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case CMD_SMSTEST:
|
||||||
|
mainParams.runSMSTest = XP_TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -64,9 +64,8 @@ void sendOnClose( XWStreamCtxt* stream, void* closure );
|
||||||
|
|
||||||
void catFinalScores( const CommonGlobals* cGlobals, XP_S16 quitter );
|
void catFinalScores( const CommonGlobals* cGlobals, XP_S16 quitter );
|
||||||
XP_Bool file_exists( const char* fileName );
|
XP_Bool file_exists( const char* fileName );
|
||||||
XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name,
|
XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name );
|
||||||
void* closure );
|
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals );
|
||||||
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals, void* closure );
|
|
||||||
void writeToFile( XWStreamCtxt* stream, void* closure );
|
void writeToFile( XWStreamCtxt* stream, void* closure );
|
||||||
XP_Bool getDictPath( const LaunchParams *params, const char* name,
|
XP_Bool getDictPath( const LaunchParams *params, const char* name,
|
||||||
char* result, int resultLen );
|
char* result, int resultLen );
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- compile-command: "make -j MEMDEBUG=TRUE";-*- */
|
/* -*- compile-command: "make -j MEMDEBUG=TRUE";-*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2006-2009 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 2006 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -20,15 +20,18 @@
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/inotify.h>
|
|
||||||
|
|
||||||
#include "linuxsms.h"
|
#include "linuxsms.h"
|
||||||
|
#include "linuxutl.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
#include "smsproto.h"
|
||||||
|
#include "linuxmain.h"
|
||||||
|
|
||||||
#define SMS_DIR "/tmp/xw_sms"
|
#define SMS_DIR "/tmp/xw_sms"
|
||||||
#define LOCK_FILE ".lock"
|
#define LOCK_FILE ".lock"
|
||||||
|
@ -54,26 +57,34 @@
|
||||||
|
|
||||||
#define ADDR_FMT "from: %s %d\n"
|
#define ADDR_FMT "from: %s %d\n"
|
||||||
|
|
||||||
static void
|
|
||||||
makeQueuePath( const XP_UCHAR* phone, XP_U16 port,
|
|
||||||
XP_UCHAR* path, XP_U16 pathlen )
|
|
||||||
{
|
|
||||||
XP_ASSERT( 0 != port );
|
|
||||||
snprintf( path, pathlen, "%s/%s_%d", SMS_DIR, phone, port );
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _LinSMSData {
|
typedef struct _LinSMSData {
|
||||||
int fd, wd;
|
|
||||||
XP_UCHAR myQueue[256];
|
XP_UCHAR myQueue[256];
|
||||||
XP_U16 myPort;
|
XP_U16 myPort;
|
||||||
FILE* lock;
|
FILE* lock;
|
||||||
XP_U16 count;
|
|
||||||
|
|
||||||
const gchar* myPhone;
|
const gchar* myPhone;
|
||||||
const SMSProcs* procs;
|
const SMSProcs* procs;
|
||||||
void* procClosure;
|
void* procClosure;
|
||||||
|
SMSProto* protoState;
|
||||||
} LinSMSData;
|
} LinSMSData;
|
||||||
|
|
||||||
|
static void doSend( LaunchParams* params, const XP_U8* buf,
|
||||||
|
XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port,
|
||||||
|
XP_U32 gameID );
|
||||||
|
static gboolean retrySend( gpointer data );
|
||||||
|
static void sendOrRetry( LaunchParams* params, SMSMsgArray* arr, XP_U16 waitSecs,
|
||||||
|
const XP_UCHAR* phone, XP_U16 port, XP_U32 gameID );
|
||||||
|
static gint check_for_files( gpointer data );
|
||||||
|
static gint check_for_files_once( gpointer data );
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatQueuePath( const XP_UCHAR* phone, XP_U16 port, XP_UCHAR* path,
|
||||||
|
XP_U16 pathlen )
|
||||||
|
{
|
||||||
|
XP_ASSERT( 0 != port );
|
||||||
|
snprintf( path, pathlen, "%s/%s_%d", SMS_DIR, phone, port );
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum { NONE, INVITE, DATA, DEATH, ACK, } SMS_CMD;
|
typedef enum { NONE, INVITE, DATA, DEATH, ACK, } SMS_CMD;
|
||||||
#define SMS_PROTO_VERSION 0
|
#define SMS_PROTO_VERSION 0
|
||||||
|
|
||||||
|
@ -103,13 +114,24 @@ unlock_queue( LinSMSData* storage )
|
||||||
}
|
}
|
||||||
|
|
||||||
static XP_S16
|
static XP_S16
|
||||||
send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
write_fake_sms( LaunchParams* params, XWStreamCtxt* stream,
|
||||||
const XP_UCHAR* phone, XP_U16 port )
|
const XP_UCHAR* phone, XP_U16 port )
|
||||||
{
|
{
|
||||||
|
XP_S16 nSent;
|
||||||
|
XP_U16 pct = XP_RANDOM() % 100;
|
||||||
|
XP_Bool skipWrite = pct < params->smsSendFailPct;
|
||||||
|
|
||||||
|
if ( skipWrite ) {
|
||||||
|
nSent = stream_getSize( stream );
|
||||||
|
XP_LOGF( "%s(): dropping sms msg of len %d to phone %s", __func__,
|
||||||
|
nSent, phone );
|
||||||
|
} else {
|
||||||
|
LinSMSData* storage = getStorage( params );
|
||||||
const XP_U8* buf = stream_getPtr( stream );
|
const XP_U8* buf = stream_getPtr( stream );
|
||||||
XP_U16 buflen = stream_getSize( stream );
|
XP_U16 buflen = stream_getSize( stream );
|
||||||
|
XP_LOGF( "%s(phone=%s, port=%d, len=%d)", __func__, phone,
|
||||||
|
port, buflen );
|
||||||
|
|
||||||
XP_S16 nSent = -1;
|
|
||||||
XP_ASSERT( !!storage );
|
XP_ASSERT( !!storage );
|
||||||
char path[256];
|
char path[256];
|
||||||
|
|
||||||
|
@ -119,17 +141,19 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
||||||
gchar* str64 = g_base64_encode( buf, buflen );
|
gchar* str64 = g_base64_encode( buf, buflen );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
XP_U16 count = ++storage->count;
|
formatQueuePath( phone, port, path, sizeof(path) );
|
||||||
makeQueuePath( phone, port, path, sizeof(path) );
|
|
||||||
XP_LOGF( "%s: writing msg %d to %s", __func__, count, path );
|
/* Random-number-based name is fine, as we pick based on age. */
|
||||||
|
int rint = makeRandomInt();
|
||||||
g_mkdir_with_parents( path, 0777 ); /* just in case */
|
g_mkdir_with_parents( path, 0777 ); /* just in case */
|
||||||
int len = strlen( path );
|
int len = strlen( path );
|
||||||
snprintf( &path[len], sizeof(path)-len, "/%d", count );
|
snprintf( &path[len], sizeof(path)-len, "/%u", rint );
|
||||||
|
|
||||||
XP_UCHAR sms[buflen*2]; /* more like (buflen*4/3) */
|
XP_UCHAR sms[buflen*2]; /* more like (buflen*4/3) */
|
||||||
XP_U16 smslen = sizeof(sms);
|
XP_U16 smslen = sizeof(sms);
|
||||||
binToSms( sms, &smslen, buf, buflen );
|
binToSms( sms, &smslen, buf, buflen );
|
||||||
XP_ASSERT( smslen == strlen(sms) );
|
XP_ASSERT( smslen == strlen(sms) );
|
||||||
|
XP_LOGF( "%s: writing msg to %s", __func__, path );
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
XP_ASSERT( !strcmp( str64, sms ) );
|
XP_ASSERT( !strcmp( str64, sms ) );
|
||||||
|
@ -139,7 +163,8 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
||||||
XP_U16 lenout = sizeof( testout );
|
XP_U16 lenout = sizeof( testout );
|
||||||
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
|
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
|
||||||
XP_ASSERT( lenout == buflen );
|
XP_ASSERT( lenout == buflen );
|
||||||
XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) );
|
// valgrind doesn't like this; punting on figuring out
|
||||||
|
// XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE* fp = fopen( path, "w" );
|
FILE* fp = fopen( path, "w" );
|
||||||
|
@ -153,8 +178,10 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
||||||
|
|
||||||
nSent = buflen;
|
nSent = buflen;
|
||||||
|
|
||||||
|
LOG_RETURNF( "%d", nSent );
|
||||||
|
}
|
||||||
return nSent;
|
return nSent;
|
||||||
} /* linux_sms_send */
|
} /* write_fake_sms */
|
||||||
|
|
||||||
static XP_S16
|
static XP_S16
|
||||||
decodeAndDelete( LinSMSData* storage, const gchar* name,
|
decodeAndDelete( LinSMSData* storage, const gchar* name,
|
||||||
|
@ -185,6 +212,7 @@ decodeAndDelete( LinSMSData* storage, const gchar* name,
|
||||||
*strstr(eol, "\n" ) = '\0';
|
*strstr(eol, "\n" ) = '\0';
|
||||||
|
|
||||||
XP_U16 inlen = strlen(eol); /* skip \n */
|
XP_U16 inlen = strlen(eol); /* skip \n */
|
||||||
|
XP_LOGF( "%s(): decoding message from file %s", __func__, name );
|
||||||
XP_U8 out[inlen];
|
XP_U8 out[inlen];
|
||||||
XP_U16 outlen = sizeof(out);
|
XP_U16 outlen = sizeof(out);
|
||||||
XP_Bool valid = smsToBin( out, &outlen, eol, inlen );
|
XP_Bool valid = smsToBin( out, &outlen, eol, inlen );
|
||||||
|
@ -194,7 +222,8 @@ decodeAndDelete( LinSMSData* storage, const gchar* name,
|
||||||
nRead = outlen;
|
nRead = outlen;
|
||||||
addr_setType( addr, COMMS_CONN_SMS );
|
addr_setType( addr, COMMS_CONN_SMS );
|
||||||
XP_STRNCPY( addr->u.sms.phone, phone, sizeof(addr->u.sms.phone) );
|
XP_STRNCPY( addr->u.sms.phone, phone, sizeof(addr->u.sms.phone) );
|
||||||
XP_LOGF( "%s: message came from phone: %s, port: %d", __func__, phone, port );
|
XP_LOGF( "%s: message came from phone: %s, port: %d", __func__,
|
||||||
|
phone, port );
|
||||||
addr->u.sms.port = port;
|
addr->u.sms.port = port;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,13 +263,24 @@ static void
|
||||||
dispatch_data( LinSMSData* storage, XP_U16 XP_UNUSED(proto),
|
dispatch_data( LinSMSData* storage, XP_U16 XP_UNUSED(proto),
|
||||||
XWStreamCtxt* stream, const CommsAddrRec* addr )
|
XWStreamCtxt* stream, const CommsAddrRec* addr )
|
||||||
{
|
{
|
||||||
|
LOG_FUNC();
|
||||||
|
|
||||||
XP_U32 gameID = stream_getU32( stream );
|
XP_U32 gameID = stream_getU32( stream );
|
||||||
XP_U16 len = stream_getSize( stream );
|
XP_U16 len = stream_getSize( stream );
|
||||||
XP_U8 data[len];
|
XP_U8 data[len];
|
||||||
stream_getBytes( stream, data, len );
|
stream_getBytes( stream, data, len );
|
||||||
|
|
||||||
(*storage->procs->msgReceived)( storage->procClosure, addr, gameID,
|
const XP_UCHAR* fromPhone = addr->u.sms.phone;
|
||||||
|
SMSMsgArray* arr = smsproto_prepInbound( storage->protoState, fromPhone,
|
||||||
data, len );
|
data, len );
|
||||||
|
if ( NULL != arr ) {
|
||||||
|
for ( XP_U16 ii = 0; ii < arr->nMsgs; ++ii ) {
|
||||||
|
SMSMsg* msg = &arr->msgs[ii];
|
||||||
|
(*storage->procs->msgReceived)( storage->procClosure, addr, gameID,
|
||||||
|
msg->data, msg->len );
|
||||||
|
}
|
||||||
|
smsproto_freeMsgArray( storage->protoState, arr );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -248,9 +288,8 @@ parseAndDispatch( LaunchParams* params, uint8_t* buf, int len,
|
||||||
CommsAddrRec* addr )
|
CommsAddrRec* addr )
|
||||||
{
|
{
|
||||||
LinSMSData* storage = getStorage( params );
|
LinSMSData* storage = getStorage( params );
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||||
params->vtMgr,
|
params->vtMgr );
|
||||||
NULL, CHANNEL_NONE, NULL );
|
|
||||||
stream_setVersion( stream, CUR_STREAM_VERS );
|
stream_setVersion( stream, CUR_STREAM_VERS );
|
||||||
stream_putBytes( stream, buf, len );
|
stream_putBytes( stream, buf, len );
|
||||||
|
|
||||||
|
@ -274,66 +313,6 @@ parseAndDispatch( LaunchParams* params, uint8_t* buf, int len,
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
sms_receive( GIOChannel *source, GIOCondition XP_UNUSED_DBG(condition), gpointer data )
|
|
||||||
{
|
|
||||||
LOG_FUNC();
|
|
||||||
XP_ASSERT( 0 != (G_IO_IN & condition) );
|
|
||||||
LaunchParams* params = (LaunchParams*)data;
|
|
||||||
XP_ASSERT( !!params->smsStorage );
|
|
||||||
LinSMSData* storage = getStorage( params );
|
|
||||||
|
|
||||||
int socket = g_io_channel_unix_get_fd( source );
|
|
||||||
XP_ASSERT( socket == storage->fd );
|
|
||||||
|
|
||||||
lock_queue( storage );
|
|
||||||
|
|
||||||
/* read required or we'll just get the event again. But we don't care
|
|
||||||
about the result or the buffer contents. */
|
|
||||||
XP_U8 buffer[sizeof(struct inotify_event) + 16];
|
|
||||||
if ( 0 > read( socket, buffer, sizeof(buffer) ) ) {
|
|
||||||
XP_LOGF( "%s: discarding inotify buffer", __func__ );
|
|
||||||
}
|
|
||||||
for ( ; ; ) {
|
|
||||||
XP_S16 nRead = -1;
|
|
||||||
char shortest[256] = { '\0' };
|
|
||||||
GDir* dir = g_dir_open( storage->myQueue, 0, NULL );
|
|
||||||
XP_LOGF( "%s: opening queue %s", __func__, storage->myQueue );
|
|
||||||
for ( ; ; ) {
|
|
||||||
const gchar* name = g_dir_read_name( dir );
|
|
||||||
if ( NULL == name ) {
|
|
||||||
break;
|
|
||||||
} else if ( 0 == strcmp( name, LOCK_FILE ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( !shortest[0] || 0 < strcmp( shortest, name ) ) {
|
|
||||||
snprintf( shortest, sizeof(shortest), "%s", name );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_dir_close( dir );
|
|
||||||
|
|
||||||
uint8_t buf[256];
|
|
||||||
CommsAddrRec fromAddr = {0};
|
|
||||||
if ( !!shortest[0] ) {
|
|
||||||
XP_LOGF( "%s: decoding message %s", __func__, shortest );
|
|
||||||
nRead = decodeAndDelete( storage, shortest, buf,
|
|
||||||
sizeof(buf), &fromAddr );
|
|
||||||
} else {
|
|
||||||
XP_LOGF( "%s: never found shortest", __func__ );
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock_queue( storage );
|
|
||||||
|
|
||||||
if ( 0 < nRead ) {
|
|
||||||
parseAndDispatch( params, buf, nRead, &fromAddr );
|
|
||||||
lock_queue( storage );
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
} /* sms_receive */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort,
|
linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort,
|
||||||
const SMSProcs* procs, void* procClosure )
|
const SMSProcs* procs, void* procClosure )
|
||||||
|
@ -346,18 +325,19 @@ linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort,
|
||||||
storage->myPort = myPort;
|
storage->myPort = myPort;
|
||||||
storage->procs = procs;
|
storage->procs = procs;
|
||||||
storage->procClosure = procClosure;
|
storage->procClosure = procClosure;
|
||||||
|
storage->protoState = smsproto_init( MPPARM(params->mpool) params->dutil );
|
||||||
|
|
||||||
makeQueuePath( myPhone, myPort, storage->myQueue, sizeof(storage->myQueue) );
|
formatQueuePath( myPhone, myPort, storage->myQueue, sizeof(storage->myQueue) );
|
||||||
XP_LOGF( "%s: my queue: %s", __func__, storage->myQueue );
|
XP_LOGF( "%s: my queue: %s", __func__, storage->myQueue );
|
||||||
storage->myPort = params->connInfo.sms.port;
|
storage->myPort = params->connInfo.sms.port;
|
||||||
|
|
||||||
(void)g_mkdir_with_parents( storage->myQueue, 0777 );
|
(void)g_mkdir_with_parents( storage->myQueue, 0777 );
|
||||||
|
|
||||||
int fd = inotify_init();
|
/* Look for preexisting or new files every half second. Easier than
|
||||||
storage->fd = fd;
|
inotify, especially when you add the need to handle files written while
|
||||||
storage->wd = inotify_add_watch( fd, storage->myQueue, IN_MODIFY );
|
not running. */
|
||||||
|
(void)g_idle_add( check_for_files_once, params );
|
||||||
(*procs->socketAdded)( params, fd, sms_receive );
|
(void)g_timeout_add( 500, check_for_files, params );
|
||||||
} /* linux_sms_init */
|
} /* linux_sms_init */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -368,8 +348,7 @@ linux_sms_invite( LaunchParams* params, const CurGameInfo* gi,
|
||||||
{
|
{
|
||||||
LOG_FUNC();
|
LOG_FUNC();
|
||||||
XWStreamCtxt* stream;
|
XWStreamCtxt* stream;
|
||||||
stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr,
|
stream = mem_stream_make_raw( MPPARM(params->mpool) params->vtMgr );
|
||||||
NULL, CHANNEL_NONE, NULL );
|
|
||||||
writeHeader( stream, INVITE );
|
writeHeader( stream, INVITE );
|
||||||
stream_putU32( stream, gi->gameID );
|
stream_putU32( stream, gi->gameID );
|
||||||
stringToStream( stream, gameName );
|
stringToStream( stream, gameName );
|
||||||
|
@ -381,8 +360,7 @@ linux_sms_invite( LaunchParams* params, const CurGameInfo* gi,
|
||||||
|
|
||||||
addrToStream( stream, addr );
|
addrToStream( stream, addr );
|
||||||
|
|
||||||
LinSMSData* storage = getStorage( params );
|
write_fake_sms( params, stream, toPhone, toPort );
|
||||||
send_sms( storage, stream, toPhone, toPort );
|
|
||||||
|
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
}
|
}
|
||||||
|
@ -391,26 +369,161 @@ XP_S16
|
||||||
linux_sms_send( LaunchParams* params, const XP_U8* buf,
|
linux_sms_send( LaunchParams* params, const XP_U8* buf,
|
||||||
XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port,
|
XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port,
|
||||||
XP_U32 gameID )
|
XP_U32 gameID )
|
||||||
|
{
|
||||||
|
LinSMSData* storage = getStorage( params );
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, buf, buflen,
|
||||||
|
phone, XP_TRUE, &waitSecs );
|
||||||
|
sendOrRetry( params, arr, waitSecs, phone, port, gameID );
|
||||||
|
return buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
doSend( LaunchParams* params, const XP_U8* buf,
|
||||||
|
XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port,
|
||||||
|
XP_U32 gameID )
|
||||||
{
|
{
|
||||||
XP_LOGF( "%s(len=%d)", __func__, buflen );
|
XP_LOGF( "%s(len=%d)", __func__, buflen );
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr,
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||||
NULL, CHANNEL_NONE, NULL );
|
params->vtMgr );
|
||||||
writeHeader( stream, DATA );
|
writeHeader( stream, DATA );
|
||||||
stream_putU32( stream, gameID );
|
stream_putU32( stream, gameID );
|
||||||
stream_putBytes( stream, buf, buflen );
|
stream_putBytes( stream, buf, buflen );
|
||||||
|
|
||||||
LinSMSData* storage = getStorage( params );
|
(void)write_fake_sms( params, stream, phone, port );
|
||||||
if ( 0 >= send_sms( storage, stream, phone, port ) ) {
|
|
||||||
buflen = -1;
|
|
||||||
}
|
|
||||||
stream_destroy( stream );
|
stream_destroy( stream );
|
||||||
|
|
||||||
return buflen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _RetryClosure {
|
||||||
|
LaunchParams* params;
|
||||||
|
XP_U16 port;
|
||||||
|
XP_U32 gameID;
|
||||||
|
XP_UCHAR phone[32];
|
||||||
|
} RetryClosure;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sendOrRetry( LaunchParams* params, SMSMsgArray* arr, XP_U16 waitSecs,
|
||||||
|
const XP_UCHAR* phone, XP_U16 port, XP_U32 gameID )
|
||||||
|
{
|
||||||
|
if ( !!arr ) {
|
||||||
|
for ( XP_U16 ii = 0; ii < arr->nMsgs; ++ii ) {
|
||||||
|
SMSMsg* msg = &arr->msgs[ii];
|
||||||
|
doSend( params, msg->data, msg->len, phone, port, gameID );
|
||||||
|
}
|
||||||
|
|
||||||
|
LinSMSData* storage = getStorage( params );
|
||||||
|
smsproto_freeMsgArray( storage->protoState, arr );
|
||||||
|
} else if ( waitSecs > 0 ) {
|
||||||
|
RetryClosure* closure = (RetryClosure*)XP_CALLOC( params->mpool,
|
||||||
|
sizeof(*closure) );
|
||||||
|
closure->params = params;
|
||||||
|
XP_STRCAT( closure->phone, phone );
|
||||||
|
closure->port = port;
|
||||||
|
closure->gameID = gameID;
|
||||||
|
g_timeout_add_seconds( 5, retrySend, closure );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
retrySend( gpointer data )
|
||||||
|
{
|
||||||
|
RetryClosure* closure = (RetryClosure*)data;
|
||||||
|
LinSMSData* storage = getStorage( closure->params );
|
||||||
|
XP_U16 waitSecs;
|
||||||
|
SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, NULL, 0,
|
||||||
|
closure->phone, XP_TRUE, &waitSecs );
|
||||||
|
sendOrRetry( closure->params, arr, waitSecs, closure->phone,
|
||||||
|
closure->port, closure->gameID );
|
||||||
|
XP_FREEP( closure->params->mpool, &closure );
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
check_for_files( gpointer data )
|
||||||
|
{
|
||||||
|
check_for_files_once( data );
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
check_for_files_once( gpointer data )
|
||||||
|
{
|
||||||
|
LOG_FUNC();
|
||||||
|
LaunchParams* params = (LaunchParams*)data;
|
||||||
|
LinSMSData* storage = getStorage( params );
|
||||||
|
|
||||||
|
for ( ; ; ) {
|
||||||
|
lock_queue( storage );
|
||||||
|
|
||||||
|
char oldestFile[256] = { '\0' };
|
||||||
|
struct timespec oldestModTime;
|
||||||
|
|
||||||
|
GDir* dir = g_dir_open( storage->myQueue, 0, NULL );
|
||||||
|
XP_LOGF( "%s: opening queue %s", __func__, storage->myQueue );
|
||||||
|
for ( ; ; ) {
|
||||||
|
const gchar* name = g_dir_read_name( dir );
|
||||||
|
if ( NULL == name ) {
|
||||||
|
break;
|
||||||
|
} else if ( 0 == strcmp( name, LOCK_FILE ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We want the oldest file first. Timestamp comes from stat(). */
|
||||||
|
struct stat statbuf;
|
||||||
|
char fullPath[500];
|
||||||
|
snprintf( fullPath, sizeof(fullPath), "%s/%s", storage->myQueue, name );
|
||||||
|
int err = stat( fullPath, &statbuf );
|
||||||
|
if ( err != 0 ) {
|
||||||
|
XP_LOGF( "%s(); %d from stat (error: %s)", __func__,
|
||||||
|
err, strerror(errno) );
|
||||||
|
XP_ASSERT( 0 );
|
||||||
|
} else {
|
||||||
|
XP_Bool replace = !oldestFile[0]; /* always replace empty/unset :-) */
|
||||||
|
if ( !replace ) {
|
||||||
|
if (statbuf.st_mtim.tv_sec == oldestModTime.tv_sec ) {
|
||||||
|
replace = statbuf.st_mtim.tv_nsec < oldestModTime.tv_nsec;
|
||||||
|
} else {
|
||||||
|
replace = statbuf.st_mtim.tv_sec < oldestModTime.tv_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( replace ) {
|
||||||
|
oldestModTime = statbuf.st_mtim;
|
||||||
|
if ( !!oldestFile[0] ) {
|
||||||
|
XP_LOGF( "%s(): replacing %s with older %s", __func__, oldestFile, name );
|
||||||
|
}
|
||||||
|
snprintf( oldestFile, sizeof(oldestFile), "%s", name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_dir_close( dir );
|
||||||
|
|
||||||
|
uint8_t buf[256];
|
||||||
|
CommsAddrRec fromAddr = {0};
|
||||||
|
XP_S16 nRead = -1;
|
||||||
|
if ( !!oldestFile[0] ) {
|
||||||
|
nRead = decodeAndDelete( storage, oldestFile, buf,
|
||||||
|
sizeof(buf), &fromAddr );
|
||||||
|
} else {
|
||||||
|
XP_LOGF( "%s: no file found", __func__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_queue( storage );
|
||||||
|
|
||||||
|
if ( 0 >= nRead ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseAndDispatch( params, buf, nRead, &fromAddr );
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
} /* check_for_files_once */
|
||||||
|
|
||||||
void
|
void
|
||||||
linux_sms_cleanup( LaunchParams* params )
|
linux_sms_cleanup( LaunchParams* params )
|
||||||
{
|
{
|
||||||
|
LinSMSData* storage = getStorage( params );
|
||||||
|
smsproto_free( storage->protoState );
|
||||||
XP_FREEP( params->mpool, ¶ms->smsStorage );
|
XP_FREEP( params->mpool, ¶ms->smsStorage );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -266,151 +266,13 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, XP_U16 nCols,
|
||||||
return result;
|
return result;
|
||||||
} /* linux_util_getSquareBonus */
|
} /* linux_util_getSquareBonus */
|
||||||
|
|
||||||
static XP_U32
|
static XW_DUtilCtxt*
|
||||||
linux_util_getCurSeconds( XW_UtilCtxt* XP_UNUSED(uc) )
|
linux_util_getDevUtilCtxt( XW_UtilCtxt* uc )
|
||||||
{
|
|
||||||
return (XP_U32)time(NULL);//tv.tv_sec;
|
|
||||||
} /* gtk_util_getCurSeconds */
|
|
||||||
|
|
||||||
static const XP_UCHAR*
|
|
||||||
linux_util_getUserString( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 code )
|
|
||||||
{
|
|
||||||
switch( code ) {
|
|
||||||
case STRD_REMAINING_TILES_ADD:
|
|
||||||
return (XP_UCHAR*)"+ %d [all remaining tiles]";
|
|
||||||
case STRD_UNUSED_TILES_SUB:
|
|
||||||
return (XP_UCHAR*)"- %d [unused tiles]";
|
|
||||||
case STR_COMMIT_CONFIRM:
|
|
||||||
return (XP_UCHAR*)"Are you sure you want to commit the current move?\n";
|
|
||||||
case STRD_TURN_SCORE:
|
|
||||||
return (XP_UCHAR*)"Score for turn: %d\n";
|
|
||||||
case STR_BONUS_ALL:
|
|
||||||
return (XP_UCHAR*)"Bonus for using all tiles: 50\n";
|
|
||||||
case STR_LOCAL_NAME:
|
|
||||||
return (XP_UCHAR*)"%s";
|
|
||||||
case STR_NONLOCAL_NAME:
|
|
||||||
return (XP_UCHAR*)"%s (remote)";
|
|
||||||
case STRD_TIME_PENALTY_SUB:
|
|
||||||
return (XP_UCHAR*)" - %d [time]";
|
|
||||||
/* added.... */
|
|
||||||
case STRD_CUMULATIVE_SCORE:
|
|
||||||
return (XP_UCHAR*)"Cumulative score: %d\n";
|
|
||||||
case STRS_TRAY_AT_START:
|
|
||||||
return (XP_UCHAR*)"Tray at start: %s\n";
|
|
||||||
case STRS_MOVE_DOWN:
|
|
||||||
return (XP_UCHAR*)"move (from %s down)\n";
|
|
||||||
case STRS_MOVE_ACROSS:
|
|
||||||
return (XP_UCHAR*)"move (from %s across)\n";
|
|
||||||
case STRS_NEW_TILES:
|
|
||||||
return (XP_UCHAR*)"New tiles: %s\n";
|
|
||||||
case STRSS_TRADED_FOR:
|
|
||||||
return (XP_UCHAR*)"Traded %s for %s.";
|
|
||||||
case STR_PASS:
|
|
||||||
return (XP_UCHAR*)"pass\n";
|
|
||||||
case STR_PHONY_REJECTED:
|
|
||||||
return (XP_UCHAR*)"Illegal word in move; turn lost!\n";
|
|
||||||
|
|
||||||
case STRD_ROBOT_TRADED:
|
|
||||||
return (XP_UCHAR*)"%d tiles traded this turn.";
|
|
||||||
case STR_ROBOT_MOVED:
|
|
||||||
return (XP_UCHAR*)"The robot \"%s\" moved:\n";
|
|
||||||
case STRS_REMOTE_MOVED:
|
|
||||||
return (XP_UCHAR*)"Remote player \"%s\" moved:\n";
|
|
||||||
|
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
||||||
case STR_LOCALPLAYERS:
|
|
||||||
return (XP_UCHAR*)"Local players";
|
|
||||||
case STR_REMOTE:
|
|
||||||
return (XP_UCHAR*)"Remote";
|
|
||||||
#endif
|
|
||||||
case STR_TOTALPLAYERS:
|
|
||||||
return (XP_UCHAR*)"Total players";
|
|
||||||
|
|
||||||
case STRS_VALUES_HEADER:
|
|
||||||
return (XP_UCHAR*)"%s counts/values:\n";
|
|
||||||
|
|
||||||
case STRD_REMAINS_HEADER:
|
|
||||||
return (XP_UCHAR*)"%d tiles left in pool.";
|
|
||||||
case STRD_REMAINS_EXPL:
|
|
||||||
return (XP_UCHAR*)"%d tiles left in pool and hidden trays:\n";
|
|
||||||
|
|
||||||
case STRSD_RESIGNED:
|
|
||||||
return "[Resigned] %s: %d";
|
|
||||||
case STRSD_WINNER:
|
|
||||||
return "[Winner] %s: %d";
|
|
||||||
case STRDSD_PLACER:
|
|
||||||
return "[#%d] %s: %d";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return (XP_UCHAR*)"unknown code to linux_util_getUserString";
|
|
||||||
}
|
|
||||||
} /* linux_util_getUserString */
|
|
||||||
|
|
||||||
static const XP_UCHAR*
|
|
||||||
linux_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 code,
|
|
||||||
XP_U16 XP_UNUSED(quantity) )
|
|
||||||
{
|
|
||||||
return linux_util_getUserString( uc, code );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef XWFEATURE_DEVID
|
|
||||||
static const XP_UCHAR*
|
|
||||||
linux_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
|
|
||||||
{
|
{
|
||||||
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
||||||
return linux_getDevID( cGlobals->params, typ );
|
return cGlobals->params->dutil;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
linux_util_deviceRegistered( XW_UtilCtxt* uc, DevIDType typ,
|
|
||||||
const XP_UCHAR* idRelay )
|
|
||||||
{
|
|
||||||
/* Script discon_ok2.sh is grepping for these strings in logs, so don't
|
|
||||||
change them! */
|
|
||||||
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
|
||||||
switch( typ ) {
|
|
||||||
case ID_TYPE_NONE: /* error case */
|
|
||||||
XP_LOGF( "%s: id rejected", __func__ );
|
|
||||||
cGlobals->params->lDevID = NULL;
|
|
||||||
break;
|
|
||||||
case ID_TYPE_RELAY:
|
|
||||||
if ( !!cGlobals->pDb && 0 < strlen( idRelay ) ) {
|
|
||||||
XP_LOGF( "%s: new id: %s", __func__, idRelay );
|
|
||||||
db_store( cGlobals->pDb, KEY_RDEVID, idRelay );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
XP_ASSERT(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef COMMS_CHECKSUM
|
|
||||||
static XP_UCHAR*
|
|
||||||
linux_util_md5sum( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len )
|
|
||||||
{
|
|
||||||
gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, len );
|
|
||||||
XP_U16 sumlen = 1 + strlen( sum );
|
|
||||||
XP_UCHAR* result = XP_MALLOC( uc->mpool, sumlen );
|
|
||||||
XP_MEMCPY( result, sum, sumlen );
|
|
||||||
g_free( sum );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef XWFEATURE_SMS
|
|
||||||
static XP_Bool
|
|
||||||
linux_util_phoneNumbersSame( XW_UtilCtxt* uc, const XP_UCHAR* p1,
|
|
||||||
const XP_UCHAR* p2 )
|
|
||||||
{
|
|
||||||
XP_USE( uc );
|
|
||||||
XP_Bool result = 0 == strcmp( p1, p2 );
|
|
||||||
XP_LOGF( "%s(%s, %s) => %d", __func__, p1, p2, result );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
void
|
||||||
linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
|
linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
|
||||||
{
|
{
|
||||||
|
@ -421,19 +283,8 @@ linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
|
||||||
|
|
||||||
util->vtable->m_util_makeEmptyDict = linux_util_makeEmptyDict;
|
util->vtable->m_util_makeEmptyDict = linux_util_makeEmptyDict;
|
||||||
util->vtable->m_util_getSquareBonus = linux_util_getSquareBonus;
|
util->vtable->m_util_getSquareBonus = linux_util_getSquareBonus;
|
||||||
util->vtable->m_util_getCurSeconds = linux_util_getCurSeconds;
|
|
||||||
util->vtable->m_util_getUserString = linux_util_getUserString;
|
util->vtable->m_util_getDevUtilCtxt = linux_util_getDevUtilCtxt;
|
||||||
util->vtable->m_util_getUserQuantityString = linux_util_getUserQuantityString;
|
|
||||||
#ifdef XWFEATURE_DEVID
|
|
||||||
util->vtable->m_util_getDevID = linux_util_getDevID;
|
|
||||||
util->vtable->m_util_deviceRegistered = linux_util_deviceRegistered;
|
|
||||||
#endif
|
|
||||||
#ifdef COMMS_CHECKSUM
|
|
||||||
util->vtable->m_util_md5sum = linux_util_md5sum;
|
|
||||||
#endif
|
|
||||||
#ifdef XWFEATURE_SMS
|
|
||||||
util->vtable->m_util_phoneNumbersSame = linux_util_phoneNumbersSame;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -666,16 +517,6 @@ storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 msglen,
|
||||||
return inUse;
|
return inUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
XWStreamCtxt*
|
|
||||||
make_simple_stream( CommonGlobals* cGlobals )
|
|
||||||
{
|
|
||||||
XWStreamCtxt* stream =
|
|
||||||
mem_stream_make( MPPARM(cGlobals->util->mpool)
|
|
||||||
cGlobals->params->vtMgr,
|
|
||||||
cGlobals, CHANNEL_NONE, NULL );
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
|
writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
|
||||||
{
|
{
|
||||||
|
@ -688,7 +529,8 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
|
||||||
guint nMsgs = g_slist_length( list );
|
guint nMsgs = g_slist_length( list );
|
||||||
XP_ASSERT( 0 < nMsgs );
|
XP_ASSERT( 0 < nMsgs );
|
||||||
|
|
||||||
XWStreamCtxt* stream = make_simple_stream( cGlobals );
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||||
|
cGlobals->params->vtMgr );
|
||||||
stream_putU16( stream, 1 ); /* number of relayIDs */
|
stream_putU16( stream, 1 ); /* number of relayIDs */
|
||||||
stream_catString( stream, relayID );
|
stream_catString( stream, relayID );
|
||||||
stream_putU8( stream, '\n' );
|
stream_putU8( stream, '\n' );
|
||||||
|
|
|
@ -48,7 +48,6 @@ void initNoConnStorage( CommonGlobals* cGlobals );
|
||||||
XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len,
|
XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len,
|
||||||
const XP_UCHAR* relayID );
|
const XP_UCHAR* relayID );
|
||||||
void writeNoConnMsgs( CommonGlobals* cGlobals, int fd );
|
void writeNoConnMsgs( CommonGlobals* cGlobals, int fd );
|
||||||
XWStreamCtxt* make_simple_stream( CommonGlobals* cGlobals );
|
|
||||||
|
|
||||||
#ifdef STREAM_VERS_BIGBOARD
|
#ifdef STREAM_VERS_BIGBOARD
|
||||||
void setSquareBonuses( const CommonGlobals* cGlobals );
|
void setSquareBonuses( const CommonGlobals* cGlobals );
|
||||||
|
@ -56,4 +55,6 @@ void setSquareBonuses( const CommonGlobals* cGlobals );
|
||||||
# define setSquareBonuses( cg )
|
# define setSquareBonuses( cg )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
XP_U32 linux_getCurSeconds();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "vtabmgr.h"
|
#include "vtabmgr.h"
|
||||||
#include "dictmgr.h"
|
#include "dictmgr.h"
|
||||||
|
#include "dutil.h"
|
||||||
|
|
||||||
typedef struct ServerInfo {
|
typedef struct ServerInfo {
|
||||||
XP_U16 nRemotePlayers;
|
XP_U16 nRemotePlayers;
|
||||||
|
@ -58,6 +59,7 @@ typedef struct LaunchParams {
|
||||||
char* dbName;
|
char* dbName;
|
||||||
sqlite3* pDb; /* null unless opened */
|
sqlite3* pDb; /* null unless opened */
|
||||||
XP_U16 saveFailPct;
|
XP_U16 saveFailPct;
|
||||||
|
XP_U16 smsSendFailPct;
|
||||||
const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS];
|
const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS];
|
||||||
#ifdef USE_SQLITE
|
#ifdef USE_SQLITE
|
||||||
char* dbFileName;
|
char* dbFileName;
|
||||||
|
@ -77,6 +79,7 @@ typedef struct LaunchParams {
|
||||||
#endif
|
#endif
|
||||||
VTableMgr* vtMgr;
|
VTableMgr* vtMgr;
|
||||||
DictMgrCtxt* dictMgr;
|
DictMgrCtxt* dictMgr;
|
||||||
|
XW_DUtilCtxt* dutil;
|
||||||
XP_U16 nLocalPlayers;
|
XP_U16 nLocalPlayers;
|
||||||
XP_U16 nHidden;
|
XP_U16 nHidden;
|
||||||
XP_U16 gameSeed;
|
XP_U16 gameSeed;
|
||||||
|
@ -106,6 +109,7 @@ typedef struct LaunchParams {
|
||||||
XP_Bool useCurses;
|
XP_Bool useCurses;
|
||||||
XP_Bool useUdp;
|
XP_Bool useUdp;
|
||||||
XP_Bool useHTTP;
|
XP_Bool useHTTP;
|
||||||
|
XP_Bool runSMSTest;
|
||||||
XP_Bool noHTTPAuto;
|
XP_Bool noHTTPAuto;
|
||||||
XP_U16 splitPackets;
|
XP_U16 splitPackets;
|
||||||
XP_U16 chatsInterval; /* 0 means disabled */
|
XP_U16 chatsInterval; /* 0 means disabled */
|
||||||
|
@ -150,7 +154,8 @@ typedef struct LaunchParams {
|
||||||
#endif
|
#endif
|
||||||
#ifdef XWFEATURE_SMS
|
#ifdef XWFEATURE_SMS
|
||||||
struct {
|
struct {
|
||||||
const char* phone;
|
const char* myPhone;
|
||||||
|
const char* serverPhone;
|
||||||
int port;
|
int port;
|
||||||
} sms;
|
} sms;
|
||||||
#endif
|
#endif
|
||||||
|
@ -197,7 +202,6 @@ struct CommonGlobals {
|
||||||
XP_U16 lastStreamSize;
|
XP_U16 lastStreamSize;
|
||||||
XP_U16 nMissing;
|
XP_U16 nMissing;
|
||||||
XP_Bool manualFinal; /* use asked for final scores */
|
XP_Bool manualFinal; /* use asked for final scores */
|
||||||
sqlite3* pDb;
|
|
||||||
sqlite3_int64 selRow;
|
sqlite3_int64 selRow;
|
||||||
|
|
||||||
SocketAddedFunc socketAdded;
|
SocketAddedFunc socketAdded;
|
||||||
|
|
|
@ -151,7 +151,7 @@ write_callback(void *contents, size_t size, size_t nmemb, void* data)
|
||||||
ws->curSize = 1L;
|
ws->curSize = 1L;
|
||||||
}
|
}
|
||||||
|
|
||||||
XP_LOGF( "%s(size=%ld, nmemb=%ld)", __func__, size, nmemb );
|
XP_LOGF( "%s(size=%zd, nmemb=%zd)", __func__, size, nmemb );
|
||||||
size_t oldLen = ws->curSize;
|
size_t oldLen = ws->curSize;
|
||||||
const size_t newLength = size * nmemb;
|
const size_t newLength = size * nmemb;
|
||||||
XP_ASSERT( (oldLen + newLength) > 0 );
|
XP_ASSERT( (oldLen + newLength) > 0 );
|
||||||
|
@ -332,9 +332,8 @@ relaycon_invite( LaunchParams* params, XP_U32 destDevID,
|
||||||
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, destDevID );
|
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, destDevID );
|
||||||
}
|
}
|
||||||
|
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||||
params->vtMgr, params,
|
params->vtMgr );
|
||||||
CHANNEL_NONE, NULL );
|
|
||||||
nli_saveToStream( invit, stream );
|
nli_saveToStream( invit, stream );
|
||||||
XP_U16 len = stream_getSize( stream );
|
XP_U16 len = stream_getSize( stream );
|
||||||
indx += writeShort( &tmpbuf[indx], sizeof(tmpbuf) - indx, len );
|
indx += writeShort( &tmpbuf[indx], sizeof(tmpbuf) - indx, len );
|
||||||
|
@ -679,9 +678,8 @@ process( RelayConStorage* storage, XP_U8* buf, ssize_t nRead )
|
||||||
#endif
|
#endif
|
||||||
getNetLong( &ptr );
|
getNetLong( &ptr );
|
||||||
XP_U16 len = getNetShort( &ptr );
|
XP_U16 len = getNetShort( &ptr );
|
||||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(storage->params->mpool)
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(storage->params->mpool)
|
||||||
storage->params->vtMgr, storage,
|
storage->params->vtMgr );
|
||||||
CHANNEL_NONE, NULL );
|
|
||||||
stream_putBytes( stream, ptr, len );
|
stream_putBytes( stream, ptr, len );
|
||||||
NetLaunchInfo invit;
|
NetLaunchInfo invit;
|
||||||
XP_Bool success = nli_makeFromStream( &invit, stream );
|
XP_Bool success = nli_makeFromStream( &invit, stream );
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import re, os, sys, getopt, shutil, threading, requests, json, glob
|
import re, os, sys, getopt, shutil, threading, requests, json, glob
|
||||||
import argparse, datetime, random, signal, subprocess, time
|
import argparse, datetime, random, signal, subprocess, time
|
||||||
|
from shutil import rmtree
|
||||||
|
|
||||||
# LOGDIR=./$(basename $0)_logs
|
# LOGDIR=./$(basename $0)_logs
|
||||||
# APP_NEW=""
|
# APP_NEW=""
|
||||||
|
@ -155,14 +156,14 @@ def player_params(args, NLOCALS, NPLAYERS, NAME_INDX):
|
||||||
def logReaderStub(dev): dev.logReaderMain()
|
def logReaderStub(dev): dev.logReaderMain()
|
||||||
|
|
||||||
class Device():
|
class Device():
|
||||||
sConnnameMap = {}
|
|
||||||
sHasLDevIDMap = {}
|
sHasLDevIDMap = {}
|
||||||
sConnNamePat = re.compile('.*got_connect_cmd: connName: "([^"]+)".*$')
|
sConnNamePat = re.compile('.*got_connect_cmd: connName: "([^"]+)".*$')
|
||||||
sGameOverPat = re.compile('.*\[unused tiles\].*')
|
sGameOverPat = re.compile('.*\[unused tiles\].*')
|
||||||
sTilesLeftPat = re.compile('.*pool_removeTiles: (\d+) tiles left in pool')
|
sTilesLeftPoolPat = re.compile('.*pool_removeTiles: (\d+) tiles left in pool')
|
||||||
|
sTilesLeftTrayPat = re.compile('.*player \d+ now has (\d+) tiles')
|
||||||
sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*')
|
sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*')
|
||||||
|
|
||||||
def __init__(self, args, game, indx, app, params, room, db, log, nInGame):
|
def __init__(self, args, game, indx, app, params, room, peers, db, log, nInGame):
|
||||||
self.game = game
|
self.game = game
|
||||||
self.indx = indx
|
self.indx = indx
|
||||||
self.args = args
|
self.args = args
|
||||||
|
@ -176,13 +177,15 @@ class Device():
|
||||||
self.nInGame = nInGame
|
self.nInGame = nInGame
|
||||||
# runtime stuff; init now
|
# runtime stuff; init now
|
||||||
self.proc = None
|
self.proc = None
|
||||||
self.connname = None
|
self.peers = peers
|
||||||
self.devID = ''
|
self.devID = ''
|
||||||
self.launchCount = 0
|
self.launchCount = 0
|
||||||
self.allDone = False # when true, can be killed
|
self.allDone = False # when true, can be killed
|
||||||
self.nTilesLeft = None
|
self.nTilesLeftPool = None
|
||||||
|
self.nTilesLeftTray = None
|
||||||
self.relayID = None
|
self.relayID = None
|
||||||
self.relaySeed = 0
|
self.relaySeed = 0
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
with open(self.logPath, "w") as log:
|
with open(self.logPath, "w") as log:
|
||||||
log.write('New cmdline: ' + self.app + ' ' + (' '.join([str(p) for p in self.params])))
|
log.write('New cmdline: ' + self.app + ' ' + (' '.join([str(p) for p in self.params])))
|
||||||
|
@ -198,23 +201,20 @@ class Device():
|
||||||
nLines += 1
|
nLines += 1
|
||||||
log.write(line + os.linesep)
|
log.write(line + os.linesep)
|
||||||
|
|
||||||
# check for connname
|
self.locked = True
|
||||||
if not self.connname:
|
|
||||||
match = Device.sConnNamePat.match(line)
|
|
||||||
if match:
|
|
||||||
self.connname = match.group(1)
|
|
||||||
if not self.connname in Device.sConnnameMap:
|
|
||||||
Device.sConnnameMap[self.connname] = set()
|
|
||||||
Device.sConnnameMap[self.connname].add(self)
|
|
||||||
|
|
||||||
# check for game over
|
# check for game over
|
||||||
if not self.gameOver:
|
if not self.gameOver:
|
||||||
match = Device.sGameOverPat.match(line)
|
match = Device.sGameOverPat.match(line)
|
||||||
if match: self.gameOver = True
|
if match: self.gameOver = True
|
||||||
|
|
||||||
# Check every line for tiles left
|
# Check every line for tiles left in pool
|
||||||
match = Device.sTilesLeftPat.match(line)
|
match = Device.sTilesLeftPoolPat.match(line)
|
||||||
if match: self.nTilesLeft = int(match.group(1))
|
if match: self.nTilesLeftPool = int(match.group(1))
|
||||||
|
|
||||||
|
# Check every line for tiles left in tray
|
||||||
|
match = Device.sTilesLeftTrayPat.match(line)
|
||||||
|
if match: self.nTilesLeftTray = int(match.group(1))
|
||||||
|
|
||||||
if not self.relayID:
|
if not self.relayID:
|
||||||
match = Device.sRelayIDPat.match(line)
|
match = Device.sRelayIDPat.match(line)
|
||||||
|
@ -222,6 +222,8 @@ class Device():
|
||||||
self.relaySeed = int(match.group(1))
|
self.relaySeed = int(match.group(1))
|
||||||
self.relayID = match.group(2)
|
self.relayID = match.group(2)
|
||||||
|
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
# print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath);
|
# print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath);
|
||||||
|
|
||||||
def launch(self):
|
def launch(self):
|
||||||
|
@ -233,7 +235,6 @@ class Device():
|
||||||
args += [self.app] + [str(p) for p in self.params]
|
args += [self.app] + [str(p) for p in self.params]
|
||||||
if self.devID: args.extend( ' '.split(self.devID))
|
if self.devID: args.extend( ' '.split(self.devID))
|
||||||
self.launchCount += 1
|
self.launchCount += 1
|
||||||
# self.logStream = open(self.logPath, flag)
|
|
||||||
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
|
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
|
||||||
stderr = subprocess.PIPE, universal_newlines = True)
|
stderr = subprocess.PIPE, universal_newlines = True)
|
||||||
self.pid = self.proc.pid
|
self.pid = self.proc.pid
|
||||||
|
@ -278,6 +279,7 @@ class Device():
|
||||||
shutil.move(self.db, self.args.LOGDIR + '/done')
|
shutil.move(self.db, self.args.LOGDIR + '/done')
|
||||||
|
|
||||||
def send_dead(self):
|
def send_dead(self):
|
||||||
|
if self.args.ADD_RELAY:
|
||||||
JSON = json.dumps([{'relayID': self.relayID, 'seed': self.relaySeed}])
|
JSON = json.dumps([{'relayID': self.relayID, 'seed': self.relaySeed}])
|
||||||
url = 'http://%s/xw4/relay.py/kill' % (self.args.HOST)
|
url = 'http://%s/xw4/relay.py/kill' % (self.args.HOST)
|
||||||
params = {'params' : JSON}
|
params = {'params' : JSON}
|
||||||
|
@ -287,8 +289,12 @@ class Device():
|
||||||
print('got exception sending to', url, params, '; is relay.py running as apache module?')
|
print('got exception sending to', url, params, '; is relay.py running as apache module?')
|
||||||
|
|
||||||
def getTilesCount(self):
|
def getTilesCount(self):
|
||||||
return {'index': self.indx, 'nTilesLeft': self.nTilesLeft,
|
assert not self.locked
|
||||||
'launchCount': self.launchCount, 'game': self.game,
|
return {'index': self.indx,
|
||||||
|
'nTilesLeftPool': self.nTilesLeftPool,
|
||||||
|
'nTilesLeftTray': self.nTilesLeftTray,
|
||||||
|
'launchCount': self.launchCount,
|
||||||
|
'game': self.game,
|
||||||
}
|
}
|
||||||
|
|
||||||
def update_ldevid(self):
|
def update_ldevid(self):
|
||||||
|
@ -313,17 +319,15 @@ class Device():
|
||||||
|
|
||||||
def check_game(self):
|
def check_game(self):
|
||||||
if self.gameOver and not self.allDone:
|
if self.gameOver and not self.allDone:
|
||||||
allDone = False
|
|
||||||
if len(Device.sConnnameMap[self.connname]) == self.nInGame:
|
|
||||||
allDone = True
|
allDone = True
|
||||||
for dev in Device.sConnnameMap[self.connname]:
|
for dev in self.peers:
|
||||||
if dev == self: continue
|
if dev == self: continue
|
||||||
if not dev.gameOver:
|
if not dev.gameOver:
|
||||||
allDone = False
|
allDone = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if allDone:
|
if allDone:
|
||||||
for dev in Device.sConnnameMap[self.connname]:
|
for dev in self.peers:
|
||||||
assert self.game == dev.game
|
assert self.game == dev.game
|
||||||
dev.allDone = True
|
dev.allDone = True
|
||||||
|
|
||||||
|
@ -361,7 +365,9 @@ def build_cmds(args):
|
||||||
PLAT_PARMS += ['--curses', '--close-stdin']
|
PLAT_PARMS += ['--curses', '--close-stdin']
|
||||||
|
|
||||||
for GAME in range(1, args.NGAMES + 1):
|
for GAME in range(1, args.NGAMES + 1):
|
||||||
|
peers = set()
|
||||||
ROOM = 'ROOM_%.3d' % (GAME % args.NROOMS)
|
ROOM = 'ROOM_%.3d' % (GAME % args.NROOMS)
|
||||||
|
PHONE_BASE = '%.4d' % (GAME % args.NROOMS)
|
||||||
NDEVS = pick_ndevs(args)
|
NDEVS = pick_ndevs(args)
|
||||||
LOCALS = figure_locals(args, NDEVS) # as array
|
LOCALS = figure_locals(args, NDEVS) # as array
|
||||||
NPLAYERS = sum(LOCALS)
|
NPLAYERS = sum(LOCALS)
|
||||||
|
@ -376,27 +382,27 @@ def build_cmds(args):
|
||||||
DB = '{}/{:02d}_{:02d}_DB.sql3'.format(args.LOGDIR, GAME, DEV)
|
DB = '{}/{:02d}_{:02d}_DB.sql3'.format(args.LOGDIR, GAME, DEV)
|
||||||
LOG = '{}/{:02d}_{:02d}_LOG.txt'.format(args.LOGDIR, GAME, DEV)
|
LOG = '{}/{:02d}_{:02d}_LOG.txt'.format(args.LOGDIR, GAME, DEV)
|
||||||
|
|
||||||
BOARD_SIZE = ['--board-size', '15']
|
|
||||||
# if [ 0 -lt ${#APPS_OLD[@]} ]; then
|
|
||||||
# # 50% chance of starting out with old app
|
|
||||||
# NAPPS=$((1+${#APPS_OLD[*]}))
|
|
||||||
# if [ 0 -lt $((RANDOM%$NAPPS)) ]; then
|
|
||||||
# APPS[$COUNTER]=${APPS_OLD[$((RANDOM%${#APPS_OLD[*]}))]}
|
|
||||||
# BOARD_SIZE="--board-size ${BOARD_SIZES_OLD[$((RANDOM%${#BOARD_SIZES_OLD[*]}))]}"
|
|
||||||
# NEW_ARGS[$COUNTER]=""
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
|
|
||||||
PARAMS = player_params(args, NLOCALS, NPLAYERS, DEV)
|
PARAMS = player_params(args, NLOCALS, NPLAYERS, DEV)
|
||||||
PARAMS += PLAT_PARMS
|
PARAMS += PLAT_PARMS
|
||||||
PARAMS += BOARD_SIZE + ['--room', ROOM, '--trade-pct', args.TRADE_PCT, '--sort-tiles']
|
PARAMS += ['--board-size', '15', '--trade-pct', args.TRADE_PCT, '--sort-tiles']
|
||||||
if args.UNDO_PCT > 0:
|
|
||||||
PARAMS += ['--undo-pct', args.UNDO_PCT]
|
# We SHOULD support having both SMS and relay working...
|
||||||
PARAMS += [ '--game-dict', DICT, '--relay-port', args.PORT, '--host', args.HOST]
|
if args.ADD_RELAY:
|
||||||
PARAMS += ['--slow-robot', '1:3', '--skip-confirm']
|
PARAMS += [ '--relay-port', args.PORT, '--room', ROOM, '--host', args.HOST]
|
||||||
PARAMS += ['--db', DB]
|
|
||||||
if random.randint(0,100) % 100 < g_UDP_PCT_START:
|
if random.randint(0,100) % 100 < g_UDP_PCT_START:
|
||||||
PARAMS += ['--use-udp']
|
PARAMS += ['--use-udp']
|
||||||
|
if args.ADD_SMS:
|
||||||
|
PARAMS += [ '--sms-number', PHONE_BASE + str(DEV - 1) ]
|
||||||
|
if args.SMS_FAIL_PCT > 0:
|
||||||
|
PARAMS += [ '--sms-fail-pct', args.SMS_FAIL_PCT ]
|
||||||
|
if DEV > 1:
|
||||||
|
PARAMS += [ '--server-sms-number', PHONE_BASE + '0' ]
|
||||||
|
|
||||||
|
if args.UNDO_PCT > 0:
|
||||||
|
PARAMS += ['--undo-pct', args.UNDO_PCT]
|
||||||
|
PARAMS += [ '--game-dict', DICT]
|
||||||
|
PARAMS += ['--slow-robot', '1:3', '--skip-confirm']
|
||||||
|
PARAMS += ['--db', DB]
|
||||||
|
|
||||||
PARAMS += ['--drop-nth-packet', g_DROP_N]
|
PARAMS += ['--drop-nth-packet', g_DROP_N]
|
||||||
if random.randint(0, 100) < args.HTTP_PCT:
|
if random.randint(0, 100) < args.HTTP_PCT:
|
||||||
|
@ -424,224 +430,14 @@ def build_cmds(args):
|
||||||
|
|
||||||
# print('PARAMS:', PARAMS)
|
# print('PARAMS:', PARAMS)
|
||||||
|
|
||||||
dev = Device(args, GAME, COUNTER, args.APP_NEW, PARAMS, ROOM, DB, LOG, len(LOCALS))
|
dev = Device(args, GAME, COUNTER, args.APP_NEW, PARAMS, ROOM, peers, DB, LOG, len(LOCALS))
|
||||||
|
peers.add(dev)
|
||||||
dev.update_ldevid()
|
dev.update_ldevid()
|
||||||
devs.append(dev)
|
devs.append(dev)
|
||||||
|
|
||||||
COUNTER += 1
|
COUNTER += 1
|
||||||
return devs
|
return devs
|
||||||
|
|
||||||
# read_resume_cmds() {
|
|
||||||
# COUNTER=0
|
|
||||||
# for LOG in $(ls $LOGDIR/*.txt); do
|
|
||||||
# echo "need to parse cmd and deal with changes"
|
|
||||||
# exit 1
|
|
||||||
# CMD=$(head -n 1 $LOG)
|
|
||||||
|
|
||||||
# ARGS[$COUNTER]=$CMD
|
|
||||||
# LOGS[$COUNTER]=$LOG
|
|
||||||
# PIDS[$COUNTER]=0
|
|
||||||
|
|
||||||
# set $CMD
|
|
||||||
# while [ $# -gt 0 ]; do
|
|
||||||
# case $1 in
|
|
||||||
# --file)
|
|
||||||
# FILES[$COUNTER]=$2
|
|
||||||
# shift
|
|
||||||
# ;;
|
|
||||||
# --room)
|
|
||||||
# ROOMS[$COUNTER]=$2
|
|
||||||
# shift
|
|
||||||
# ;;
|
|
||||||
# esac
|
|
||||||
# shift
|
|
||||||
# done
|
|
||||||
# COUNTER=$((COUNTER+1))
|
|
||||||
# done
|
|
||||||
# ROOM_PIDS[$ROOM]=0
|
|
||||||
# }
|
|
||||||
|
|
||||||
# launch() {
|
|
||||||
# KEY=$1
|
|
||||||
# LOG=${LOGS[$KEY]}
|
|
||||||
# APP="${APPS[$KEY]}"
|
|
||||||
# if [ -z "$APP" ]; then
|
|
||||||
# echo "error: no app set"
|
|
||||||
# exit 1
|
|
||||||
# fi
|
|
||||||
# PARAMS="${NEW_ARGS[$KEY]} ${ARGS[$KEY]} ${ARGS_DEVID[$KEY]}"
|
|
||||||
# exec $APP $PARAMS >/dev/null 2>>$LOG
|
|
||||||
# }
|
|
||||||
|
|
||||||
# # launch_via_rq() {
|
|
||||||
# # KEY=$1
|
|
||||||
# # RELAYID=$2
|
|
||||||
# # PIPE=${PIPES[$KEY]}
|
|
||||||
# # ../relay/rq -f $RELAYID -o $PIPE &
|
|
||||||
# # CMD="${CMDS[$KEY]}"
|
|
||||||
# # exec $CMD >/dev/null 2>>$LOG
|
|
||||||
# # }
|
|
||||||
|
|
||||||
# send_dead() {
|
|
||||||
# ID=$1
|
|
||||||
# DB=${FILES[$ID]}
|
|
||||||
# while :; do
|
|
||||||
# [ -f $DB ] || break # it's gone
|
|
||||||
# RES=$(echo 'select relayid, seed from games limit 1;' | sqlite3 -separator ' ' $DB || /bin/true)
|
|
||||||
# [ -n "$RES" ] && break
|
|
||||||
# sleep 0.2
|
|
||||||
# done
|
|
||||||
# RELAYID=$(echo $RES | awk '{print $1}')
|
|
||||||
# SEED=$(echo $RES | awk '{print $2}')
|
|
||||||
# JSON="[{\"relayID\":\"$RELAYID\", \"seed\":$SEED}]"
|
|
||||||
# curl -G --data-urlencode params="$JSON" http://$HOST/xw4/relay.py/kill >/dev/null 2>&1
|
|
||||||
# }
|
|
||||||
|
|
||||||
# close_device() {
|
|
||||||
# ID=$1
|
|
||||||
# MVTO=$2
|
|
||||||
# REASON="$3"
|
|
||||||
# PID=${PIDS[$ID]}
|
|
||||||
# if [ $PID -ne 0 ]; then
|
|
||||||
# kill ${PIDS[$ID]} 2>/dev/null
|
|
||||||
# wait ${PIDS[$ID]}
|
|
||||||
# ROOM=${ROOMS[$ID]}
|
|
||||||
# [ ${ROOM_PIDS[$ROOM]} -eq $PID ] && ROOM_PIDS[$ROOM]=0
|
|
||||||
# fi
|
|
||||||
# unset PIDS[$ID]
|
|
||||||
# unset ARGS[$ID]
|
|
||||||
# echo "closing game: $REASON" >> ${LOGS[$ID]}
|
|
||||||
# if [ -n "$MVTO" ]; then
|
|
||||||
# [ -f "${FILES[$ID]}" ] && mv ${FILES[$ID]} $MVTO
|
|
||||||
# mv ${LOGS[$ID]} $MVTO
|
|
||||||
# else
|
|
||||||
# rm -f ${FILES[$ID]}
|
|
||||||
# rm -f ${LOGS[$ID]}
|
|
||||||
# fi
|
|
||||||
# unset FILES[$ID]
|
|
||||||
# unset LOGS[$ID]
|
|
||||||
# unset ROOMS[$ID]
|
|
||||||
# unset APPS[$ID]
|
|
||||||
# unset ARGS_DEVID[$ID]
|
|
||||||
|
|
||||||
# COUNT=${#ARGS[*]}
|
|
||||||
# echo "$COUNT devices left playing..."
|
|
||||||
# }
|
|
||||||
|
|
||||||
# OBITS=""
|
|
||||||
|
|
||||||
# kill_from_log() {
|
|
||||||
# LOG=$1
|
|
||||||
# RELAYID=$(./scripts/relayID.sh --long $LOG)
|
|
||||||
# if [ -n "$RELAYID" ]; then
|
|
||||||
# OBITS="$OBITS -d $RELAYID"
|
|
||||||
# if [ 0 -eq $(($RANDOM%2)) ]; then
|
|
||||||
# ../relay/rq -a $HOST $OBITS 2>/dev/null || /bin/true
|
|
||||||
# OBITS=""
|
|
||||||
# fi
|
|
||||||
# return 0 # success
|
|
||||||
# fi
|
|
||||||
# echo "unable to send kill command for $LOG"
|
|
||||||
# return 1
|
|
||||||
# }
|
|
||||||
|
|
||||||
# maybe_resign() {
|
|
||||||
# if [ "$RESIGN_PCT" -gt 0 ]; then
|
|
||||||
# KEY=$1
|
|
||||||
# LOG=${LOGS[$KEY]}
|
|
||||||
# if grep -aq XWRELAY_ALLHERE $LOG; then
|
|
||||||
# if [ $((${RANDOM}%100)) -lt $RESIGN_PCT ]; then
|
|
||||||
# echo "making $LOG $(connName $LOG) resign..."
|
|
||||||
# kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" || /bin/true
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# }
|
|
||||||
|
|
||||||
# try_upgrade() {
|
|
||||||
# KEY=$1
|
|
||||||
# if [ 0 -lt ${#APPS_OLD[@]} ]; then
|
|
||||||
# if [ $APP_NEW != "${APPS[$KEY]}" ]; then
|
|
||||||
# # one in five chance of upgrading
|
|
||||||
# if [ 0 -eq $((RANDOM % UPGRADE_ODDS)) ]; then
|
|
||||||
# APPS[$KEY]=$APP_NEW
|
|
||||||
# NEW_ARGS[$KEY]="$APP_NEW_PARAMS"
|
|
||||||
# print_cmdline $KEY
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# }
|
|
||||||
|
|
||||||
# try_upgrade_upd() {
|
|
||||||
# KEY=$1
|
|
||||||
# CMD=${ARGS[$KEY]}
|
|
||||||
# if [ "${CMD/--use-udp/}" = "${CMD}" ]; then
|
|
||||||
# if [ $((RANDOM % 100)) -lt $UDP_PCT_INCR ]; then
|
|
||||||
# ARGS[$KEY]="$CMD --use-udp"
|
|
||||||
# echo -n "$(date +%r): "
|
|
||||||
# echo "upgrading key $KEY to use UDP"
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# }
|
|
||||||
|
|
||||||
# check_game() {
|
|
||||||
# KEY=$1
|
|
||||||
# LOG=${LOGS[$KEY]}
|
|
||||||
# CONNNAME="$(connName $LOG)"
|
|
||||||
# OTHERS=""
|
|
||||||
# if [ -n "$CONNNAME" ]; then
|
|
||||||
# if grep -aq '\[unused tiles\]' $LOG ; then
|
|
||||||
# for INDX in ${!LOGS[*]}; do
|
|
||||||
# [ $INDX -eq $KEY ] && continue
|
|
||||||
# ALOG=${LOGS[$INDX]}
|
|
||||||
# CONNNAME2="$(connName $ALOG)"
|
|
||||||
# if [ "$CONNNAME2" = "$CONNNAME" ]; then
|
|
||||||
# if ! grep -aq '\[unused tiles\]' $ALOG; then
|
|
||||||
# OTHERS=""
|
|
||||||
# break
|
|
||||||
# fi
|
|
||||||
# OTHERS="$OTHERS $INDX"
|
|
||||||
# fi
|
|
||||||
# done
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# if [ -n "$OTHERS" ]; then
|
|
||||||
# echo -n "Closing $CONNNAME [$(date)]: "
|
|
||||||
# # kill_from_logs $OTHERS $KEY
|
|
||||||
# for ID in $OTHERS $KEY; do
|
|
||||||
# echo -n "${ID}:${LOGS[$ID]}, "
|
|
||||||
# kill_from_log ${LOGS[$ID]} || /bin/true
|
|
||||||
# send_dead $ID
|
|
||||||
# close_device $ID $DONEDIR "game over"
|
|
||||||
# done
|
|
||||||
# echo ""
|
|
||||||
# # XWRELAY_ERROR_DELETED may be old
|
|
||||||
# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then
|
|
||||||
# echo "deleting $LOG $(connName $LOG) b/c another resigned"
|
|
||||||
# kill_from_log $LOG || /bin/true
|
|
||||||
# close_device $KEY $DEADDIR "other resigned"
|
|
||||||
# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then
|
|
||||||
# echo "deleting $LOG $(connName $LOG) b/c another resigned"
|
|
||||||
# kill_from_log $LOG || /bin/true
|
|
||||||
# close_device $KEY $DEADDIR "other resigned"
|
|
||||||
# else
|
|
||||||
# maybe_resign $KEY
|
|
||||||
# fi
|
|
||||||
# }
|
|
||||||
|
|
||||||
# increment_drop() {
|
|
||||||
# KEY=$1
|
|
||||||
# CMD=${ARGS[$KEY]}
|
|
||||||
# if [ "$CMD" != "${CMD/drop-nth-packet//}" ]; then
|
|
||||||
# DROP_N=$(echo $CMD | sed 's,^.*drop-nth-packet \(-*[0-9]*\) .*$,\1,')
|
|
||||||
# if [ $DROP_N -gt 0 ]; then
|
|
||||||
# NEXT_N=$((DROP_N+1))
|
|
||||||
# ARGS[$KEY]=$(echo $CMD | sed "s,^\(.*drop-nth-packet \)$DROP_N\(.*\)$,\1$NEXT_N\2,")
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# }
|
|
||||||
|
|
||||||
def summarizeTileCounts(devs, endTime, state):
|
def summarizeTileCounts(devs, endTime, state):
|
||||||
global gDeadLaunches
|
global gDeadLaunches
|
||||||
shouldGoOn = True
|
shouldGoOn = True
|
||||||
|
@ -675,13 +471,22 @@ def summarizeTileCounts(devs, endTime, state):
|
||||||
nLaunches += launchCount
|
nLaunches += launchCount
|
||||||
fmtData[1]['data'].append('{:{width}d}'.format(launchCount, width=colWidth))
|
fmtData[1]['data'].append('{:{width}d}'.format(launchCount, width=colWidth))
|
||||||
|
|
||||||
nTiles = datum['nTilesLeft']
|
# Format tiles left. It's the number in the bag/pool until
|
||||||
fmtData[2]['data'].append(nTiles is None and ('-' * colWidth) or '{:{width}d}'.format(nTiles, width=colWidth))
|
# that drops to 0, then the number in the tray preceeded by
|
||||||
if not nTiles is None: totalTiles += int(nTiles)
|
# '+'. Only the pool number is included in the totalTiles sum.
|
||||||
|
nTilesPool = datum['nTilesLeftPool']
|
||||||
|
nTilesTray = datum['nTilesLeftTray']
|
||||||
|
if nTilesPool is None and nTilesTray is None:
|
||||||
|
txt = ('-' * colWidth)
|
||||||
|
elif int(nTilesPool) == 0 and not nTilesTray is None:
|
||||||
|
txt = '{:+{width}d}'.format(nTilesTray, width=colWidth-1)
|
||||||
|
else:
|
||||||
|
txt = '{:{width}d}'.format(nTilesPool, width=colWidth)
|
||||||
|
totalTiles += int(nTilesPool)
|
||||||
|
fmtData[2]['data'].append(txt)
|
||||||
|
|
||||||
print('')
|
print('')
|
||||||
print('devs left: {}; tiles left: {}; total launches: {}; {}/{}'
|
print('devs left: {}; bag tiles left: {}; total launches: {}; {}/{}'
|
||||||
.format(nDevs, totalTiles, nLaunches, datetime.datetime.now(), endTime ))
|
.format(nDevs, totalTiles, nLaunches, datetime.datetime.now(), endTime ))
|
||||||
fmt = '{head:>%d} {data}' % headWidth
|
fmt = '{head:>%d} {data}' % headWidth
|
||||||
for datum in fmtData: datum['data'] = ' '.join(datum['data'])
|
for datum in fmtData: datum['data'] = ' '.join(datum['data'])
|
||||||
|
@ -708,19 +513,21 @@ gDone = False
|
||||||
def run_cmds(args, devs):
|
def run_cmds(args, devs):
|
||||||
nCores = countCores()
|
nCores = countCores()
|
||||||
endTime = datetime.datetime.now() + datetime.timedelta(minutes = args.TIMEOUT_MINS)
|
endTime = datetime.datetime.now() + datetime.timedelta(minutes = args.TIMEOUT_MINS)
|
||||||
LOOPCOUNT = 0
|
|
||||||
printState = {}
|
printState = {}
|
||||||
|
lastPrint = datetime.datetime.now()
|
||||||
|
|
||||||
while len(devs) > 0 and not gDone:
|
while len(devs) > 0 and not gDone:
|
||||||
if countCores() > nCores:
|
if countCores() > nCores:
|
||||||
print('core file count increased; exiting')
|
print('core file count increased; exiting')
|
||||||
break
|
break
|
||||||
if datetime.datetime.now() > endTime:
|
now = datetime.datetime.now()
|
||||||
|
if now > endTime:
|
||||||
print('outta time; outta here')
|
print('outta time; outta here')
|
||||||
break
|
break
|
||||||
|
|
||||||
LOOPCOUNT += 1
|
# print stats every 5 seconds
|
||||||
if 0 == LOOPCOUNT % 20:
|
if now - lastPrint > datetime.timedelta(seconds = 5):
|
||||||
|
lastPrint = now
|
||||||
if not summarizeTileCounts(devs, endTime, printState):
|
if not summarizeTileCounts(devs, endTime, printState):
|
||||||
print('no change in too long; exiting')
|
print('no change in too long; exiting')
|
||||||
break
|
break
|
||||||
|
@ -742,20 +549,16 @@ def run_cmds(args, devs):
|
||||||
# PIDS[$KEY]=$PID
|
# PIDS[$KEY]=$PID
|
||||||
# ROOM_PIDS[$ROOM]=$PID
|
# ROOM_PIDS[$ROOM]=$PID
|
||||||
# MINEND[$KEY]=$(($NOW + $MINRUN))
|
# MINEND[$KEY]=$(($NOW + $MINRUN))
|
||||||
elif not dev.minTimeExpired():
|
elif dev.minTimeExpired():
|
||||||
# print('sleeping...')
|
|
||||||
time.sleep(1.0)
|
|
||||||
else:
|
|
||||||
dev.kill()
|
dev.kill()
|
||||||
if dev.handleAllDone():
|
if dev.handleAllDone():
|
||||||
devs.remove(dev)
|
devs.remove(dev)
|
||||||
# if g_DROP_N >= 0: dev.increment_drop()
|
else:
|
||||||
# update_ldevid $KEY
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
|
||||||
# if we get here via a break, kill any remaining games
|
# if we get here via a break, kill any remaining games
|
||||||
if devs:
|
if devs:
|
||||||
print('stopping %d remaining games' % (len(devs)))
|
print('stopping {} remaining games'.format(len(devs)))
|
||||||
for dev in devs:
|
for dev in devs:
|
||||||
if dev.running(): dev.kill()
|
if dev.running(): dev.kill()
|
||||||
|
|
||||||
|
@ -828,6 +631,7 @@ def mkParser():
|
||||||
help = 'No game will have fewer devices than this')
|
help = 'No game will have fewer devices than this')
|
||||||
parser.add_argument('--max-devs', dest = 'MAXDEVS', type = int, default = 4,
|
parser.add_argument('--max-devs', dest = 'MAXDEVS', type = int, default = 4,
|
||||||
help = 'No game will have more devices than this')
|
help = 'No game will have more devices than this')
|
||||||
|
|
||||||
parser.add_argument('--min-run', dest = 'MINRUN', type = int, default = 2,
|
parser.add_argument('--min-run', dest = 'MINRUN', type = int, default = 2,
|
||||||
help = 'Keep each run alive at least this many seconds')
|
help = 'Keep each run alive at least this many seconds')
|
||||||
# # echo " [--new-app <path/to/app] \\" >&2
|
# # echo " [--new-app <path/to/app] \\" >&2
|
||||||
|
@ -852,6 +656,10 @@ def mkParser():
|
||||||
parser.add_argument('--undo-pct', dest = 'UNDO_PCT', default = 0, type = int)
|
parser.add_argument('--undo-pct', dest = 'UNDO_PCT', default = 0, type = int)
|
||||||
parser.add_argument('--trade-pct', dest = 'TRADE_PCT', default = 0, type = int)
|
parser.add_argument('--trade-pct', dest = 'TRADE_PCT', default = 0, type = int)
|
||||||
|
|
||||||
|
parser.add_argument('--add-sms', dest = 'ADD_SMS', default = False, action = 'store_true')
|
||||||
|
parser.add_argument('--sms-fail-pct', dest = 'SMS_FAIL_PCT', default = 0, type = int)
|
||||||
|
parser.add_argument('--remove-relay', dest = 'ADD_RELAY', default = True, action = 'store_false')
|
||||||
|
|
||||||
parser.add_argument('--with-valgrind', dest = 'VALGRIND', default = False,
|
parser.add_argument('--with-valgrind', dest = 'VALGRIND', default = False,
|
||||||
action = 'store_true')
|
action = 'store_true')
|
||||||
|
|
||||||
|
@ -1058,6 +866,10 @@ def main():
|
||||||
signal.signal(signal.SIGINT, termHandler)
|
signal.signal(signal.SIGINT, termHandler)
|
||||||
|
|
||||||
args = parseArgs()
|
args = parseArgs()
|
||||||
|
# Hack: old files confuse things. Remove is simple fix good for now
|
||||||
|
if args.ADD_SMS:
|
||||||
|
try: rmtree('/tmp/xw_sms')
|
||||||
|
except: None
|
||||||
devs = build_cmds(args)
|
devs = build_cmds(args)
|
||||||
nDevs = len(devs)
|
nDevs = len(devs)
|
||||||
run_cmds(args, devs)
|
run_cmds(args, devs)
|
||||||
|
|
|
@ -1135,7 +1135,7 @@ DBMgr::StoreMessage( const char* const connName, int destHid,
|
||||||
|
|
||||||
void
|
void
|
||||||
DBMgr::decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
DBMgr::decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
||||||
int byteaIndex, uint8_t* buf, size_t* buflen )
|
int byteaIndex, vector<uint8_t>& buf )
|
||||||
{
|
{
|
||||||
const char* from = NULL;
|
const char* from = NULL;
|
||||||
if ( useB64 ) {
|
if ( useB64 ) {
|
||||||
|
@ -1146,22 +1146,19 @@ DBMgr::decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
||||||
from = PQgetvalue( result, rowIndx, byteaIndex );
|
from = PQgetvalue( result, rowIndx, byteaIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t to_length;
|
|
||||||
if ( useB64 ) {
|
if ( useB64 ) {
|
||||||
gsize out_len;
|
gsize out_len;
|
||||||
guchar* txt = g_base64_decode( (const gchar*)from, &out_len );
|
guchar* txt = g_base64_decode( (const gchar*)from, &out_len );
|
||||||
to_length = out_len;
|
buf.insert( buf.end(), txt, txt + out_len );
|
||||||
assert( to_length <= *buflen );
|
assert( buf.size() == out_len );
|
||||||
memcpy( buf, txt, to_length );
|
|
||||||
g_free( txt );
|
g_free( txt );
|
||||||
} else {
|
} else {
|
||||||
uint8_t* bytes = PQunescapeBytea( (const uint8_t*)from,
|
size_t to_length;
|
||||||
&to_length );
|
uint8_t* bytes = PQunescapeBytea( (const uint8_t*)from, &to_length );
|
||||||
assert( to_length <= *buflen );
|
buf.insert( buf.end(), bytes, bytes + to_length );
|
||||||
memcpy( buf, bytes, to_length );
|
assert( buf.size() == to_length );
|
||||||
PQfreemem( bytes );
|
PQfreemem( bytes );
|
||||||
}
|
}
|
||||||
*buflen = to_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1193,12 +1190,9 @@ DBMgr::storedMessagesImpl( string test, vector<DBMgr::MsgInfo>& msgs,
|
||||||
bool hasConnname = connname != NULL && '\0' != connname[0];
|
bool hasConnname = connname != NULL && '\0' != connname[0];
|
||||||
MsgInfo msg( id, token, hasConnname );
|
MsgInfo msg( id, token, hasConnname );
|
||||||
|
|
||||||
uint8_t buf[1024];
|
decodeMessage( result, m_useB64, ii, 1, 2, msg.msg );
|
||||||
size_t buflen = sizeof(buf);
|
|
||||||
decodeMessage( result, m_useB64, ii, 1, 2, buf, &buflen );
|
|
||||||
size_t msglen = atoi( PQgetvalue( result, ii, 3 ) );
|
size_t msglen = atoi( PQgetvalue( result, ii, 3 ) );
|
||||||
assert( 0 == msglen || buflen == msglen );
|
assert( 0 == msglen || msg.msg.size() == msglen );
|
||||||
msg.msg.insert( msg.msg.end(), buf, &buf[buflen] );
|
|
||||||
msgs.push_back( msg );
|
msgs.push_back( msg );
|
||||||
}
|
}
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
|
|
|
@ -166,7 +166,8 @@ class DBMgr {
|
||||||
int getCountWhere( const char* table, string& test );
|
int getCountWhere( const char* table, string& test );
|
||||||
void RemoveStoredMessages( string& msgIDs );
|
void RemoveStoredMessages( string& msgIDs );
|
||||||
void decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
void decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
||||||
int byteaIndex, uint8_t* buf, size_t* buflen );
|
int byteaIndex, vector<uint8_t>& buf );
|
||||||
|
|
||||||
void storedMessagesImpl( string query, vector<DBMgr::MsgInfo>& msgs,
|
void storedMessagesImpl( string query, vector<DBMgr::MsgInfo>& msgs,
|
||||||
bool nullConnnameOK );
|
bool nullConnnameOK );
|
||||||
int CountStoredMessages( const char* const connName, int hid );
|
int CountStoredMessages( const char* const connName, int hid );
|
||||||
|
|
|
@ -57,8 +57,7 @@ DevMgr::rememberDevice( DevIDRelay devid, const AddrInfo::AddrUnion* saddr )
|
||||||
pair<map<DevIDRelay,UDPAddrRec>::iterator, bool> result =
|
pair<map<DevIDRelay,UDPAddrRec>::iterator, bool> result =
|
||||||
m_devAddrMap.insert( pair<DevIDRelay,UDPAddrRec>( devid, rec ) );
|
m_devAddrMap.insert( pair<DevIDRelay,UDPAddrRec>( devid, rec ) );
|
||||||
if ( !result.second ) {
|
if ( !result.second ) {
|
||||||
logf( XW_LOGINFO, "%s: replacing address for %d; was %s, now %s",
|
logf( XW_LOGINFO, "%s: replacing address for %d", __func__, devid );
|
||||||
__func__, devid, result.first->second, rec );
|
|
||||||
result.first->second = rec;
|
result.first->second = rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,16 +35,16 @@ UDPAckTrack::nextPacketID( XWRelayReg cmd )
|
||||||
{
|
{
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
if ( shouldAck( cmd ) ) {
|
if ( shouldAck( cmd ) ) {
|
||||||
result = get()->nextPacketIDImpl();
|
result = get()->nextPacketIDImpl( cmd );
|
||||||
assert( PACKETID_NONE != result );
|
assert( PACKETID_NONE != result );
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static*/ void
|
/* static*/ string
|
||||||
UDPAckTrack::recordAck( uint32_t packetID )
|
UDPAckTrack::recordAck( uint32_t packetID )
|
||||||
{
|
{
|
||||||
get()->recordAckImpl( packetID );
|
return get()->recordAckImpl( packetID );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
|
@ -97,33 +97,37 @@ UDPAckTrack::ackLimit()
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
UDPAckTrack::nextPacketIDImpl()
|
UDPAckTrack::nextPacketIDImpl( XWRelayReg cmd )
|
||||||
{
|
{
|
||||||
MutexLock ml( &m_mutex );
|
MutexLock ml( &m_mutex );
|
||||||
uint32_t result = ++m_nextID;
|
uint32_t result = ++m_nextID;
|
||||||
AckRecord record;
|
AckRecord record( cmd , result );
|
||||||
m_pendings.insert( pair<uint32_t,AckRecord>(result, record) );
|
m_pendings.insert( pair<uint32_t,AckRecord>(result, record) );
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
string
|
||||||
UDPAckTrack::recordAckImpl( uint32_t packetID )
|
UDPAckTrack::recordAckImpl( uint32_t packetID )
|
||||||
{
|
{
|
||||||
|
string str;
|
||||||
map<uint32_t, AckRecord>::iterator iter;
|
map<uint32_t, AckRecord>::iterator iter;
|
||||||
MutexLock ml( &m_mutex );
|
MutexLock ml( &m_mutex );
|
||||||
iter = m_pendings.find( packetID );
|
iter = m_pendings.find( packetID );
|
||||||
if ( m_pendings.end() == iter ) {
|
if ( m_pendings.end() == iter ) {
|
||||||
logf( XW_LOGERROR, "%s: packet ID %d not found", __func__, packetID );
|
logf( XW_LOGERROR, "%s: packet ID %d not found", __func__, packetID );
|
||||||
} else {
|
} else {
|
||||||
time_t took = time( NULL ) - iter->second.m_createTime;
|
AckRecord& rec = iter->second;
|
||||||
|
str = rec.toStr();
|
||||||
|
time_t took = time( NULL ) - rec.m_createTime;
|
||||||
if ( 5 < took ) {
|
if ( 5 < took ) {
|
||||||
logf( XW_LOGERROR, "%s: packet ID %d took %d seconds to get acked",
|
logf( XW_LOGERROR, "%s: packet %s took %d seconds to get acked",
|
||||||
__func__, packetID, took );
|
__func__, str.c_str(), took );
|
||||||
}
|
}
|
||||||
|
|
||||||
callProc( iter, true );
|
callProc( iter, true );
|
||||||
m_pendings.erase( iter );
|
m_pendings.erase( iter );
|
||||||
}
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -134,8 +138,8 @@ UDPAckTrack::setOnAckImpl( OnAckProc proc, uint32_t packetID, void* data )
|
||||||
MutexLock ml( &m_mutex );
|
MutexLock ml( &m_mutex );
|
||||||
map<uint32_t, AckRecord>::iterator iter = m_pendings.find( packetID );
|
map<uint32_t, AckRecord>::iterator iter = m_pendings.find( packetID );
|
||||||
if ( m_pendings.end() != iter ) {
|
if ( m_pendings.end() != iter ) {
|
||||||
iter->second.proc = proc;
|
iter->second.m_proc = proc;
|
||||||
iter->second.data = data;
|
iter->second.m_data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return canAdd;
|
return canAdd;
|
||||||
|
@ -180,12 +184,12 @@ void
|
||||||
UDPAckTrack::callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked )
|
UDPAckTrack::callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked )
|
||||||
{
|
{
|
||||||
const AckRecord* record = &(iter->second);
|
const AckRecord* record = &(iter->second);
|
||||||
OnAckProc proc = record->proc;
|
OnAckProc proc = record->m_proc;
|
||||||
if ( NULL != proc ) {
|
if ( NULL != proc ) {
|
||||||
uint32_t packetID = iter->first;
|
uint32_t packetID = iter->first;
|
||||||
logf( XW_LOGINFO, "%s(packetID=%d, acked=%d, proc=%p)", __func__,
|
logf( XW_LOGINFO, "%s(packetID=%d, acked=%d, proc=%p)", __func__,
|
||||||
packetID, acked, proc );
|
packetID, acked, proc );
|
||||||
(*proc)( acked, packetID, record->data );
|
(*proc)( acked, packetID, record->m_data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,15 +199,16 @@ UDPAckTrack::threadProc()
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
time_t limit = ackLimit();
|
time_t limit = ackLimit();
|
||||||
sleep( limit / 2 );
|
sleep( limit / 2 );
|
||||||
vector<uint32_t> older;
|
vector<string> older;
|
||||||
{
|
{
|
||||||
MutexLock ml( &m_mutex );
|
MutexLock ml( &m_mutex );
|
||||||
time_t now = time( NULL );
|
time_t now = time( NULL );
|
||||||
map<uint32_t, AckRecord>::iterator iter;
|
map<uint32_t, AckRecord>::iterator iter;
|
||||||
for ( iter = m_pendings.begin(); m_pendings.end() != iter; ) {
|
for ( iter = m_pendings.begin(); m_pendings.end() != iter; ) {
|
||||||
time_t took = now - iter->second.m_createTime;
|
AckRecord& rec = iter->second;
|
||||||
|
time_t took = now - rec.m_createTime;
|
||||||
if ( limit < took ) {
|
if ( limit < took ) {
|
||||||
older.push_back( iter->first );
|
older.push_back( rec.toStr() );
|
||||||
callProc( iter, false );
|
callProc( iter, false );
|
||||||
m_pendings.erase( iter++ );
|
m_pendings.erase( iter++ );
|
||||||
} else {
|
} else {
|
||||||
|
@ -212,14 +217,14 @@ UDPAckTrack::threadProc()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( 0 < older.size() ) {
|
if ( 0 < older.size() ) {
|
||||||
StrWPF leaked;
|
string leaked;
|
||||||
vector<uint32_t>::const_iterator iter = older.begin();
|
vector<string>::const_iterator iter = older.begin();
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
leaked.catf( "%d", *iter );
|
leaked += iter->c_str();
|
||||||
if ( ++iter == older.end() ) {
|
if ( ++iter == older.end() ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
leaked.catf( ", " );
|
leaked += ", ";
|
||||||
}
|
}
|
||||||
logf( XW_LOGERROR, "%s: these packets leaked (were not ack'd "
|
logf( XW_LOGERROR, "%s: these packets leaked (were not ack'd "
|
||||||
"within %d seconds): %s", __func__,
|
"within %d seconds): %s", __func__,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
/* -*- compile-command: "make -j3"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved.
|
* Copyright 2013 - 2018 by Eric House (xwords@eehouse.org). All rights
|
||||||
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -20,6 +21,8 @@
|
||||||
#ifndef _UDPACK_H_
|
#ifndef _UDPACK_H_
|
||||||
#define _UDPACK_H_
|
#define _UDPACK_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "xwrelay_priv.h"
|
#include "xwrelay_priv.h"
|
||||||
#include "xwrelay.h"
|
#include "xwrelay.h"
|
||||||
#include "strwpf.h"
|
#include "strwpf.h"
|
||||||
|
@ -28,10 +31,26 @@ typedef void (*OnAckProc)( bool acked, uint32_t packetID, void* data );
|
||||||
|
|
||||||
class AckRecord {
|
class AckRecord {
|
||||||
public:
|
public:
|
||||||
AckRecord() { m_createTime = time( NULL ); proc = NULL; }
|
AckRecord( XWRelayReg cmd, uint32_t id ) {
|
||||||
|
m_createTime = time( NULL );
|
||||||
|
m_proc = NULL;
|
||||||
|
m_cmd = cmd;
|
||||||
|
m_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
string toStr()
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
sprintf( buf, "%d/%s", m_id, msgToStr( m_cmd ) );
|
||||||
|
string str(buf);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
time_t m_createTime;
|
time_t m_createTime;
|
||||||
OnAckProc proc;
|
OnAckProc m_proc;
|
||||||
void* data;
|
XWRelayReg m_cmd;
|
||||||
|
void* m_data;
|
||||||
|
private:
|
||||||
|
uint32_t m_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UDPAckTrack {
|
class UDPAckTrack {
|
||||||
|
@ -39,7 +58,7 @@ class UDPAckTrack {
|
||||||
static const uint32_t PACKETID_NONE = 0;
|
static const uint32_t PACKETID_NONE = 0;
|
||||||
|
|
||||||
static uint32_t nextPacketID( XWRelayReg cmd );
|
static uint32_t nextPacketID( XWRelayReg cmd );
|
||||||
static void recordAck( uint32_t packetID );
|
static string recordAck( uint32_t packetID );
|
||||||
static bool setOnAck( OnAckProc proc, uint32_t packetID, void* data );
|
static bool setOnAck( OnAckProc proc, uint32_t packetID, void* data );
|
||||||
static bool shouldAck( XWRelayReg cmd );
|
static bool shouldAck( XWRelayReg cmd );
|
||||||
/* called from ctrl port */
|
/* called from ctrl port */
|
||||||
|
@ -51,8 +70,8 @@ class UDPAckTrack {
|
||||||
static void* thread_main( void* arg );
|
static void* thread_main( void* arg );
|
||||||
UDPAckTrack();
|
UDPAckTrack();
|
||||||
time_t ackLimit();
|
time_t ackLimit();
|
||||||
uint32_t nextPacketIDImpl();
|
uint32_t nextPacketIDImpl( XWRelayReg cmd );
|
||||||
void recordAckImpl( uint32_t packetID );
|
string recordAckImpl( uint32_t packetID );
|
||||||
bool setOnAckImpl( OnAckProc proc, uint32_t packetID, void* data );
|
bool setOnAckImpl( OnAckProc proc, uint32_t packetID, void* data );
|
||||||
void callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked );
|
void callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked );
|
||||||
void printAcksImpl( StrWPF& out );
|
void printAcksImpl( StrWPF& out );
|
||||||
|
|
|
@ -1702,7 +1702,7 @@ retrieveMessages( DevID& devID, const AddrInfo* addr )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char*
|
const char*
|
||||||
msgToStr( XWRelayReg msg )
|
msgToStr( XWRelayReg msg )
|
||||||
{
|
{
|
||||||
const char* str;
|
const char* str;
|
||||||
|
@ -1840,7 +1840,7 @@ handle_udp_packet( PacketThreadClosure* ptc )
|
||||||
}
|
}
|
||||||
|
|
||||||
case XWPDEV_KEEPALIVE:
|
case XWPDEV_KEEPALIVE:
|
||||||
case XWPDEV_RQSTMSGS: { // here
|
case XWPDEV_RQSTMSGS: {
|
||||||
DevID devID( ID_TYPE_RELAY );
|
DevID devID( ID_TYPE_RELAY );
|
||||||
if ( getVLIString( &ptr, end, devID.m_devIDString ) ) {
|
if ( getVLIString( &ptr, end, devID.m_devIDString ) ) {
|
||||||
const AddrInfo* addr = ptc->addr();
|
const AddrInfo* addr = ptc->addr();
|
||||||
|
@ -1855,8 +1855,8 @@ handle_udp_packet( PacketThreadClosure* ptc )
|
||||||
case XWPDEV_ACK: {
|
case XWPDEV_ACK: {
|
||||||
uint32_t packetID;
|
uint32_t packetID;
|
||||||
if ( vli2un( &ptr, end, &packetID ) ) {
|
if ( vli2un( &ptr, end, &packetID ) ) {
|
||||||
logf( XW_LOGINFO, "%s: got ack for packet %d", __func__, packetID );
|
string str = UDPAckTrack::recordAck( packetID );
|
||||||
UDPAckTrack::recordAck( packetID );
|
logf( XW_LOGINFO, "%s: got ack for packet %s", __func__, str.c_str() );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ int read_packet( int sock, uint8_t* buf, int buflen );
|
||||||
void onMsgAcked( bool acked, uint32_t packetID, void* data );
|
void onMsgAcked( bool acked, uint32_t packetID, void* data );
|
||||||
|
|
||||||
const char* cmdToStr( XWRELAY_Cmd cmd );
|
const char* cmdToStr( XWRELAY_Cmd cmd );
|
||||||
|
const char* msgToStr( XWRelayReg msg );
|
||||||
|
|
||||||
extern class ListenerMgr g_listeners;
|
extern class ListenerMgr g_listeners;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue