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
|
||||
- platform-tools
|
||||
- build-tools-27.0.3
|
||||
- android-23
|
||||
- android-26
|
||||
licenses:
|
||||
- android-sdk-preview-license-.+
|
||||
- android-sdk-license-.+
|
||||
- google-gdk-license-.+
|
||||
before_script:
|
||||
- export TERM=dumb
|
||||
- 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}
|
||||
- cd xwords4/android/
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
- openssl aes-256-cbc -K $encrypted_8436f2891714_key -iv $encrypted_8436f2891714_iv
|
||||
-in id_rsa_uploader.enc -out /tmp/id_rsa_uploader -d
|
||||
- chmod 600 \/tmp\/id_rsa_uploader
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y python-lxml imagemagick
|
||||
script:
|
||||
- "./gradlew -PuseCrashlytics assXw4dDeb"
|
||||
- "./gradlew -PuseCrashlytics asXw4dDeb"
|
||||
- scp -o "StrictHostKeyChecking no" -i /tmp/id_rsa_uploader -d app/build/outputs/apk/xw4d/debug/*.apk
|
||||
uploader@eehouse.org:XW4D_UPLOAD
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- xwords@eehouse.org
|
||||
on_success: always
|
||||
on_failure: always
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
def INITIAL_CLIENT_VERS = 8
|
||||
def VERSION_CODE_BASE = 132
|
||||
def VERSION_NAME = '4.4.136'
|
||||
def VERSION_CODE_BASE = 133
|
||||
def VERSION_NAME = '4.4.137'
|
||||
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
|
||||
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
|
||||
def BUILD_INFO_NAME = "build-info.txt"
|
||||
|
@ -24,6 +24,7 @@ if ( FABRIC_API_KEY && hasProperty('useCrashlytics') ) {
|
|||
}
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
google()
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -32,7 +33,7 @@ android {
|
|||
buildToolsVersion '27.0.3'
|
||||
defaultConfig {
|
||||
minSdkVersion 8
|
||||
targetSdkVersion 23
|
||||
targetSdkVersion 26
|
||||
versionCode VERSION_CODE_BASE
|
||||
versionName VERSION_NAME
|
||||
}
|
||||
|
|
|
@ -43,6 +43,16 @@
|
|||
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"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="standard"
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
</style>
|
||||
</head>
|
||||
<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">
|
||||
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
|
||||
|
@ -25,9 +25,12 @@
|
|||
|
||||
<h3>New with this release</h3>
|
||||
<ul>
|
||||
<li>Flip arrow to match when board flipped</li>
|
||||
<li>Fix problems when undos arrive at unexpected times</li>
|
||||
<li>Don't crash when game connects while app's in background</li>
|
||||
<li>Add new SMS-handling code. One feature isn't compatible with
|
||||
the previous release so it won't be activated until there's been
|
||||
time for everybody to upgrade.</li>
|
||||
<li>Include new Catalan translations</li>
|
||||
<li>Fix bug causing (rarely) unending notifications of wordlist
|
||||
upgrade</li>
|
||||
</ul>
|
||||
|
||||
<p>(The full changelog
|
||||
|
|
|
@ -175,8 +175,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
String[] names = { name };
|
||||
DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, names );
|
||||
m_dictClosure = XwJNI.dict_iter_init( pairs.m_bytes[0],
|
||||
name, pairs.m_paths[0],
|
||||
JNIUtilsImpl.get(m_activity) );
|
||||
name, pairs.m_paths[0] );
|
||||
|
||||
m_browseState = DBUtils.dictsGetOffset( m_activity, name, m_loc );
|
||||
boolean newState = null == m_browseState;
|
||||
|
@ -280,7 +279,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
{
|
||||
TextView text = (TextView)view;
|
||||
// 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() );
|
||||
switch ( parent.getId() ) {
|
||||
case R.id.wordlen_min:
|
||||
|
|
|
@ -486,7 +486,6 @@ public class DictLangCache {
|
|||
info = new DictInfo();
|
||||
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
||||
pairs.m_paths[0],
|
||||
JNIUtilsImpl.get( context ),
|
||||
DictLoc.DOWNLOAD == dal.loc,
|
||||
info ) ) {
|
||||
|
||||
|
|
|
@ -151,15 +151,17 @@ public class DictUtils {
|
|||
// changes?
|
||||
}
|
||||
|
||||
private static void addLogDup( Map<String, DictAndLoc> map, String path,
|
||||
DictLoc loc )
|
||||
private static void addLogDupIf( Context context, Map<String, DictAndLoc> map,
|
||||
String path, File dir, DictLoc loc )
|
||||
{
|
||||
if ( isDict( context, path, dir ) ) {
|
||||
String name = removeDictExtn( new File(path).getName() );
|
||||
if ( map.containsKey( name ) ) {
|
||||
Log.d( TAG, "replacing info for %s with from %s", name, loc );
|
||||
}
|
||||
map.put( name, new DictAndLoc( name, loc ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryDir( Context context, File dir, boolean strict,
|
||||
DictLoc loc, Map<String, DictAndLoc> map )
|
||||
|
@ -168,9 +170,7 @@ public class DictUtils {
|
|||
String[] list = dir.list();
|
||||
if ( null != list ) {
|
||||
for ( String file : list ) {
|
||||
if ( isDict( context, file, strict? dir : null ) ) {
|
||||
addLogDup( map, file, loc );
|
||||
}
|
||||
addLogDupIf( context, map, file, strict? dir : null, loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,15 +190,11 @@ public class DictUtils {
|
|||
Map<String, DictAndLoc> map = new HashMap<>();
|
||||
|
||||
for ( String file : getAssets( context ) ) {
|
||||
if ( isDict( context, file, null ) ) {
|
||||
addLogDup( map, file, DictLoc.BUILT_IN );
|
||||
}
|
||||
addLogDupIf( context, map, file, null, DictLoc.BUILT_IN );
|
||||
}
|
||||
|
||||
for ( String file : context.fileList() ) {
|
||||
if ( isDict( context, file, null ) ) {
|
||||
addLogDup( map, file, DictLoc.INTERNAL );
|
||||
}
|
||||
addLogDupIf( context, map, file, null, DictLoc.INTERNAL );
|
||||
}
|
||||
|
||||
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, map );
|
||||
|
@ -578,7 +574,7 @@ public class DictUtils {
|
|||
if ( ok && null != dir ) {
|
||||
String fullPath = new File( dir, file ).getPath();
|
||||
ok = XwJNI.dict_getInfo( null, removeDictExtn( file ), fullPath,
|
||||
JNIUtilsImpl.get(context), true, null );
|
||||
true, null );
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ public class DwnldDelegate extends ListDelegateBase {
|
|||
callListener( m_uri, true );
|
||||
} else if ( null != m_appFile ) {
|
||||
// launch the installer
|
||||
Intent intent = Utils.makeInstallIntent( m_appFile );
|
||||
Intent intent = Utils.makeInstallIntent( m_activity, m_appFile );
|
||||
startActivity( intent );
|
||||
} else {
|
||||
// we failed at something....
|
||||
|
@ -473,5 +473,4 @@ public class DwnldDelegate extends ListDelegateBase {
|
|||
intent.putExtra( APK_EXTRA, url );
|
||||
return intent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Display;
|
||||
|
@ -131,8 +131,8 @@ public class GameUtils {
|
|||
|
||||
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes, pairs.m_paths,
|
||||
gi.langName( context ), (UtilCtxt)null,
|
||||
JNIUtilsImpl.get( context ), (DrawCtx)null,
|
||||
CommonPrefs.get( context ), (TransportProcs)null );
|
||||
(DrawCtx)null, CommonPrefs.get( context ),
|
||||
(TransportProcs)null );
|
||||
|
||||
if ( juggle ) {
|
||||
gi.juggle();
|
||||
|
@ -372,15 +372,13 @@ public class GameUtils {
|
|||
gamePtr = XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||
pairs.m_bytes, pairs.m_paths,
|
||||
langName, util,
|
||||
JNIUtilsImpl.get( context ),
|
||||
null,
|
||||
CommonPrefs.get(context),
|
||||
tp );
|
||||
if ( null == gamePtr ) {
|
||||
gamePtr = XwJNI.initNew( gi, dictNames,
|
||||
pairs.m_bytes, pairs.m_paths,
|
||||
langName, (UtilCtxt)null,
|
||||
JNIUtilsImpl.get(context), null,
|
||||
langName, (UtilCtxt)null, null,
|
||||
CommonPrefs.get(context), null );
|
||||
}
|
||||
}
|
||||
|
@ -485,12 +483,12 @@ public class GameUtils {
|
|||
}
|
||||
|
||||
if ( force ) {
|
||||
new ResendTask( context, filter, proc ).execute();
|
||||
|
||||
System.arraycopy( sendTimes, 0, /* src */
|
||||
sendTimes, 1, /* dest */
|
||||
sendTimes.length - 1 );
|
||||
sendTimes[0] = now;
|
||||
|
||||
new Resender( context, filter, proc ).start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,6 +912,7 @@ public class GameUtils {
|
|||
m_gotMsg = false;
|
||||
m_gameOver = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
||||
{
|
||||
|
@ -923,11 +922,14 @@ public class GameUtils {
|
|||
m_chat = msg;
|
||||
m_ts = tsSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void turnChanged( int newTurn )
|
||||
{
|
||||
m_gotMsg = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyGameOver()
|
||||
{
|
||||
m_gameOver = true;
|
||||
|
@ -1024,8 +1026,7 @@ public class GameUtils {
|
|||
XwJNI.initFromStream( rowid, stream, gi, dictNames,
|
||||
pairs.m_bytes, pairs.m_paths,
|
||||
gi.langName( context ), null,
|
||||
JNIUtilsImpl.get(context), null,
|
||||
CommonPrefs.get( context ), null );
|
||||
null, CommonPrefs.get( context ), null );
|
||||
// second time required as game_makeFromStream can overwrite
|
||||
gi.replaceDicts( context, newDict );
|
||||
|
||||
|
@ -1075,8 +1076,7 @@ public class GameUtils {
|
|||
new CurGameInfo(context),
|
||||
dictNames, pairs.m_bytes,
|
||||
pairs.m_paths, langName,
|
||||
null, JNIUtilsImpl.get(context),
|
||||
null, cp, null );
|
||||
null, null, cp, null );
|
||||
madeGame = null != gamePtr;
|
||||
}
|
||||
|
||||
|
@ -1084,8 +1084,7 @@ public class GameUtils {
|
|||
Assert.assertNull( gamePtr );
|
||||
gamePtr = XwJNI.initNew( gi, dictNames, pairs.m_bytes,
|
||||
pairs.m_paths, langName, util,
|
||||
JNIUtilsImpl.get(context), (DrawCtx)null,
|
||||
cp, sink );
|
||||
(DrawCtx)null, cp, sink );
|
||||
}
|
||||
|
||||
if ( null != car ) {
|
||||
|
@ -1255,23 +1254,27 @@ public class GameUtils {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static class ResendTask extends AsyncTask<Void, Void, Void> {
|
||||
private static class Resender extends Thread {
|
||||
private Context m_context;
|
||||
private ResendDoneProc m_doneProc;
|
||||
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 )
|
||||
{
|
||||
m_context = context;
|
||||
m_filter = filter;
|
||||
m_doneProc = proc;
|
||||
if ( null != proc ) {
|
||||
m_handler = new Handler();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground( Void... unused )
|
||||
public void run()
|
||||
{
|
||||
int nSentTotal = 0;
|
||||
HashMap<Long,CommsConnTypeSet> games
|
||||
= DBUtils.getGamesWithSendsPending( m_context );
|
||||
|
||||
|
@ -1296,11 +1299,11 @@ public class GameUtils {
|
|||
int nSent = XwJNI.comms_resendAll( gamePtr, true,
|
||||
m_filter, false );
|
||||
gamePtr.release();
|
||||
Log.d( TAG, "ResendTask.doInBackground(): sent %d "
|
||||
Log.d( TAG, "Resender.doInBackground(): sent %d "
|
||||
+ "messages for rowid %d", nSent, rowid );
|
||||
m_nSent += sink.numSent();
|
||||
nSentTotal += sink.numSent();
|
||||
} else {
|
||||
Log.d( TAG, "ResendTask.doInBackground(): loadMakeGame()"
|
||||
Log.d( TAG, "Resender.doInBackground(): loadMakeGame()"
|
||||
+ " failed for rowid %d", rowid );
|
||||
}
|
||||
lock.unlock();
|
||||
|
@ -1311,19 +1314,21 @@ public class GameUtils {
|
|||
false, false );
|
||||
jniThread.release();
|
||||
} else {
|
||||
Log.w( TAG, "ResendTask.doInBackground: unable to unlock %d",
|
||||
Log.w( TAG, "Resender.doInBackground: unable to unlock %d",
|
||||
rowid );
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( Void unused )
|
||||
{
|
||||
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( ONEGROUP_ITEMS, menu, 1 == nGroupsSelected );
|
||||
|
||||
// check for updates only serves release builds, so don't offer in
|
||||
// DEBUG case
|
||||
boolean enable = showDbg && nothingSelected
|
||||
&& UpdateCheckReceiver.haveToCheck( m_activity );
|
||||
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.jni.CommsAddrRec;
|
||||
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.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.
|
||||
*
|
||||
* 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.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -55,19 +56,11 @@ import java.util.Set;
|
|||
public class SMSService extends XWService {
|
||||
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_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 = 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,
|
||||
INVITE,
|
||||
SEND,
|
||||
|
@ -75,6 +68,7 @@ public class SMSService extends XWService {
|
|||
ADDED_MISSING,
|
||||
STOP_SELF,
|
||||
HANDLEDATA,
|
||||
RESEND,
|
||||
};
|
||||
|
||||
private static final String CMD_STR = "CMD";
|
||||
|
@ -98,8 +92,6 @@ public class SMSService extends XWService {
|
|||
|
||||
private int m_nReceived = 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>();
|
||||
|
||||
public static class SMSPhoneInfo {
|
||||
|
@ -366,6 +358,9 @@ public class SMSService extends XWService {
|
|||
phone = intent.getStringExtra( PHONE );
|
||||
sendDiedPacket( phone, gameID );
|
||||
break;
|
||||
case RESEND:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
resendFor( phone, null, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,18 +429,18 @@ public class SMSService extends XWService {
|
|||
dos.writeInt( gameID );
|
||||
dos.write( bytes, 0, bytes.length );
|
||||
dos.flush();
|
||||
if ( send( SMS_CMD.DATA, bas.toByteArray(), phone ) ) {
|
||||
send( SMS_CMD.DATA, bas.toByteArray(), phone );
|
||||
nSent = bytes.length;
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
}
|
||||
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
|
||||
{
|
||||
Log.d( TAG, "send(%s, len=%d)", cmd, bytes.length );
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream dos = new DataOutputStream( bas );
|
||||
dos.writeByte( SMS_PROTO_VERSION );
|
||||
|
@ -457,42 +452,44 @@ public class SMSService extends XWService {
|
|||
dos.flush();
|
||||
|
||||
byte[] data = bas.toByteArray();
|
||||
byte[][] msgs = breakAndEncode( data );
|
||||
boolean result = null != msgs && sendBuffers( msgs, phone );
|
||||
return result;
|
||||
|
||||
boolean newSMSEnabled = XWPrefs.getSMSProtoEnabled( this );
|
||||
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 count = (msg.length + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY;
|
||||
if ( count < MAX_MSG_COUNT ) {
|
||||
result = new byte[count][];
|
||||
int msgID = ++s_nSent % 0x000000FF;
|
||||
int[] waitSecs = { 0 };
|
||||
byte[][] msgs = XwJNI.smsproto_prepOutbound( data, phone, forceNow, waitSecs );
|
||||
if ( null != msgs ) {
|
||||
sendBuffers( msgs, phone );
|
||||
}
|
||||
if ( waitSecs[0] > 0 ) {
|
||||
Assert.assertFalse( forceNow );
|
||||
postResend( phone, waitSecs[0] );
|
||||
}
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for ( int ii = 0; ii < count; ++ii ) {
|
||||
int len = msg.length - end;
|
||||
if ( len > MAX_LEN_BINARY ) {
|
||||
len = MAX_LEN_BINARY;
|
||||
}
|
||||
end += len;
|
||||
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 );
|
||||
private void postResend( final String phone, final int waitSecs )
|
||||
{
|
||||
Log.d( TAG, "postResend" );
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep( waitSecs * 1000 );
|
||||
|
||||
result[ii] = part;
|
||||
start = end;
|
||||
Log.d( TAG, "postResend.run()" );
|
||||
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 )
|
||||
|
@ -534,53 +531,20 @@ public class SMSService extends XWService {
|
|||
|
||||
private void receiveBuffer( byte[] buffer, String senderPhone )
|
||||
{
|
||||
byte proto = buffer[0];
|
||||
int id = buffer[1];
|
||||
int index = buffer[2];
|
||||
int count = buffer[3];
|
||||
byte[] rest = new byte[buffer.length - 4];
|
||||
System.arraycopy( buffer, 4, rest, 0, rest.length );
|
||||
if ( tryAssemble( senderPhone, id, index, count, rest ) ) {
|
||||
byte[][] msgs = XwJNI.smsproto_prepInbound( buffer, senderPhone );
|
||||
if ( null != msgs ) {
|
||||
for ( byte[] msg : msgs ) {
|
||||
if ( !disAssemble( senderPhone, msg ) ) {
|
||||
Log.e( TAG, "failed on message from %s", senderPhone );
|
||||
}
|
||||
}
|
||||
postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||
} else {
|
||||
// Will see this when don't have SMS permission
|
||||
Log.w( TAG, "receiveBuffer(): bogus message from phone %s",
|
||||
Log.w( TAG, "receiveBuffer(): bogus or incomplete message from phone %s",
|
||||
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 )
|
||||
{
|
||||
boolean success = false;
|
||||
|
@ -685,7 +649,7 @@ public class SMSService extends XWService {
|
|||
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 );
|
||||
}
|
||||
|
||||
|
@ -777,70 +741,4 @@ public class SMSService extends XWService {
|
|||
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;
|
||||
}
|
||||
|
||||
@Override protected String doInBackground( Void... unused )
|
||||
@Override
|
||||
protected String doInBackground( Void... unused )
|
||||
{
|
||||
HttpURLConnection conn
|
||||
= NetUtils.makeHttpUpdateConn( m_context, "getUpdates" );
|
||||
|
@ -267,7 +268,8 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
|
|||
return json;
|
||||
}
|
||||
|
||||
@Override protected void onPostExecute( String json )
|
||||
@Override
|
||||
protected void onPostExecute( String json )
|
||||
{
|
||||
if ( null != json ) {
|
||||
makeNotificationsIf( json, m_params );
|
||||
|
|
|
@ -34,17 +34,16 @@ import android.content.pm.PackageManager;
|
|||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.text.ClipboardManager;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Base64;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
@ -486,13 +485,16 @@ public class Utils {
|
|||
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 = Uri.parse( withScheme );
|
||||
Uri uri = FileProvider
|
||||
.getUriForFile( context,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
file );
|
||||
Intent intent = new Intent( Intent.ACTION_VIEW );
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -501,7 +503,7 @@ public class Utils {
|
|||
{
|
||||
boolean result = false;
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent intent = makeInstallIntent( path );
|
||||
Intent intent = makeInstallIntent( context, path );
|
||||
List<ResolveInfo> doers =
|
||||
pm.queryIntentActivities( intent,
|
||||
PackageManager.MATCH_DEFAULT_ONLY );
|
||||
|
|
|
@ -82,6 +82,11 @@ public class XWPrefs {
|
|||
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 )
|
||||
{
|
||||
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 );
|
||||
JNIUtils jniUtils = JNIUtilsImpl.get( context );
|
||||
|
||||
// Assert.assertNull( m_jniGamePtr ); // fired!!
|
||||
if ( null != m_jniGamePtr ) {
|
||||
|
@ -224,15 +223,13 @@ public class JNIThread extends Thread {
|
|||
dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName( m_context ),
|
||||
utils, jniUtils,
|
||||
null, cp, m_xport );
|
||||
utils, null, cp, m_xport );
|
||||
}
|
||||
if ( null == m_jniGamePtr ) {
|
||||
m_jniGamePtr = XwJNI.initNew( m_gi, dictNames, pairs.m_bytes,
|
||||
pairs.m_paths,
|
||||
m_gi.langName(m_context),
|
||||
utils, jniUtils, null, cp,
|
||||
m_xport );
|
||||
utils, null, cp, m_xport );
|
||||
}
|
||||
Assert.assertNotNull( m_jniGamePtr );
|
||||
m_lastSavedState = Arrays.hashCode( stream );
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.content.Context;
|
|||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils;
|
||||
import org.eehouse.android.xw4.XWApp;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
|
||||
|
@ -41,14 +42,13 @@ public class JNIUtilsImpl implements JNIUtils {
|
|||
private static JNIUtilsImpl s_impl = null;
|
||||
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 ) {
|
||||
s_impl = new JNIUtilsImpl();
|
||||
s_impl = new JNIUtilsImpl( XWApp.getContext() );
|
||||
}
|
||||
s_impl.m_context = context;
|
||||
return s_impl;
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,8 @@ public class JNIUtilsImpl implements JNIUtils {
|
|||
|
||||
public String getMD5SumFor( byte[] bytes )
|
||||
{
|
||||
String result = null;
|
||||
if ( bytes != null ) {
|
||||
byte[] digest = null;
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
|
@ -150,7 +152,9 @@ public class JNIUtilsImpl implements JNIUtils {
|
|||
} catch ( java.security.NoSuchAlgorithmException nsae ) {
|
||||
Log.ex( TAG, nsae );
|
||||
}
|
||||
return Utils.digestToString( digest );
|
||||
result = Utils.digestToString( digest );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getMD5SumFor( String dictName, byte[] bytes )
|
||||
|
|
|
@ -60,50 +60,10 @@ public interface UtilCtxt {
|
|||
void remSelected();
|
||||
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 playerScoreHeld( int player );
|
||||
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 notifyTrade( String[] tiles );
|
||||
|
||||
|
@ -146,6 +106,4 @@ public interface UtilCtxt {
|
|||
boolean turnLost );
|
||||
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.DevID;
|
||||
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.loc.LocUtils;
|
||||
|
||||
public class UtilCtxtImpl implements UtilCtxt {
|
||||
private static final String TAG = UtilCtxtImpl.class.getSimpleName();
|
||||
|
@ -44,222 +39,114 @@ public class UtilCtxtImpl implements UtilCtxt {
|
|||
m_context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestTime() {
|
||||
subclassOverride( "requestTime" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPickTileBlank( int playerNum, int col, int row,
|
||||
String[] texts )
|
||||
{
|
||||
subclassOverride( "userPickTileBlank" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
|
||||
String[] texts, int[] counts )
|
||||
{
|
||||
subclassOverride( "informNeedPickTiles" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNeedPassword( int player, String name )
|
||||
{
|
||||
subclassOverride( "informNeedPassword" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void turnChanged( int newTurn )
|
||||
{
|
||||
subclassOverride( "turnChanged" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean engineProgressCallback()
|
||||
{
|
||||
// subclassOverride( "engineProgressCallback" );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimer( int why, int when, int handle )
|
||||
{
|
||||
subclassOverride( "setTimer" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTimer( int why )
|
||||
{
|
||||
subclassOverride( "clearTimer" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remSelected()
|
||||
{
|
||||
subclassOverride( "remSelected" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsServer( boolean isServer )
|
||||
{
|
||||
subclassOverride( "setIsServer" );
|
||||
}
|
||||
|
||||
public String getDevID( /*out*/ byte[] typa )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bonusSquareHeld( int bonus )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerScoreHeld( int player )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cellSquareHeld( String words )
|
||||
{
|
||||
}
|
||||
|
||||
public String getUserString( int stringCode )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMove( String query )
|
||||
{
|
||||
subclassOverride( "notifyMove" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTrade( String[] tiles )
|
||||
{
|
||||
subclassOverride( "notifyTrade" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userError( int id )
|
||||
{
|
||||
subclassOverride( "userError" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informMove( int turn, String expl, String words )
|
||||
{
|
||||
subclassOverride( "informMove" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informUndo()
|
||||
{
|
||||
subclassOverride( "informUndo" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNetDict( int lang, String oldName,
|
||||
String newName, String newSum,
|
||||
CurGameInfo.XWPhoniesChoice phonies )
|
||||
|
@ -267,6 +154,7 @@ public class UtilCtxtImpl implements UtilCtxt {
|
|||
subclassOverride( "informNetDict" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informMissing( boolean isServer,
|
||||
CommsConnTypeSet connTypes,
|
||||
int nDevices, int nMissingPlayers )
|
||||
|
@ -276,11 +164,13 @@ public class UtilCtxtImpl implements UtilCtxt {
|
|||
|
||||
// Probably want to cache the fact that the game over notification
|
||||
// showed up and then display it next time game's opened.
|
||||
@Override
|
||||
public void notifyGameOver()
|
||||
{
|
||||
subclassOverride( "notifyGameOver" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyIllegalWords( String dict, String[] words, int turn,
|
||||
boolean turnLost )
|
||||
{
|
||||
|
@ -288,17 +178,12 @@ public class UtilCtxtImpl implements UtilCtxt {
|
|||
}
|
||||
|
||||
// These need to go into some sort of chat DB, not dropped.
|
||||
@Override
|
||||
public void showChat( String msg, int fromIndx, String fromName, int tsSeconds )
|
||||
{
|
||||
subclassOverride( "showChat" );
|
||||
}
|
||||
|
||||
public boolean phoneNumbersSame( String num1, String num2 )
|
||||
{
|
||||
boolean same = PhoneNumberUtils.compare( m_context, num1, num2 );
|
||||
return same;
|
||||
}
|
||||
|
||||
private void subclassOverride( String name ) {
|
||||
// DbgUtils.logf( "%s::%s() called", getClass().getName(), name );
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
|
||||
* rights reserved.
|
||||
* Copyright 2009 - 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
|
||||
|
@ -97,7 +97,7 @@ public class XwJNI {
|
|||
private int m_ptr;
|
||||
private XwJNI()
|
||||
{
|
||||
m_ptr = initGlobals();
|
||||
m_ptr = initGlobals( new DUtilCtxt(), JNIUtilsImpl.get() );
|
||||
}
|
||||
|
||||
public static void cleanGlobals()
|
||||
|
@ -134,23 +134,30 @@ public class XwJNI {
|
|||
public static native boolean timerFired( GamePtr gamePtr, int why,
|
||||
int when, int handle );
|
||||
|
||||
// Stateless methods
|
||||
public static native byte[] gi_to_stream( CurGameInfo gi );
|
||||
public static native void gi_from_stream( CurGameInfo gi, byte[] stream );
|
||||
public static byte[] gi_to_stream( CurGameInfo gi )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
NetLaunchInfo nli = new NetLaunchInfo();
|
||||
nli_from_stream( nli, stream );
|
||||
nli_from_stream( getJNI().m_ptr, nli, stream );
|
||||
nli.unfreezeAddrs();
|
||||
return nli;
|
||||
}
|
||||
private static native void nli_from_stream( NetLaunchInfo nli, byte[] stream );
|
||||
|
||||
public static native void comms_getInitialAddr( CommsAddrRec addr,
|
||||
String relayHost,
|
||||
int relayPort );
|
||||
|
@ -160,8 +167,7 @@ public class XwJNI {
|
|||
private static GamePtr initJNI( long rowid )
|
||||
{
|
||||
int seed = Utils.nextRandomInt();
|
||||
String tag = String.format( "%d", rowid );
|
||||
int ptr = initJNI( getJNI().m_ptr, seed, tag );
|
||||
int ptr = initJNI( getJNI().m_ptr, seed );
|
||||
GamePtr result = 0 == ptr ? null : new GamePtr( ptr, rowid );
|
||||
return result;
|
||||
}
|
||||
|
@ -170,13 +176,13 @@ public class XwJNI {
|
|||
initFromStream( long rowid, byte[] stream, CurGameInfo gi,
|
||||
String[] dictNames, byte[][] dictBytes,
|
||||
String[] dictPaths, String langName,
|
||||
UtilCtxt util, JNIUtils jniu, DrawCtx draw,
|
||||
UtilCtxt util, DrawCtx draw,
|
||||
CommonPrefs cp, TransportProcs procs )
|
||||
|
||||
{
|
||||
GamePtr gamePtr = initJNI( rowid );
|
||||
if ( game_makeFromStream( gamePtr, stream, gi, dictNames, dictBytes,
|
||||
dictPaths, langName, util, jniu, draw,
|
||||
dictPaths, langName, util, draw,
|
||||
cp, procs ) ) {
|
||||
gamePtr.retain();
|
||||
} else {
|
||||
|
@ -189,12 +195,11 @@ public class XwJNI {
|
|||
public static synchronized GamePtr
|
||||
initNew( CurGameInfo gi, String[] dictNames, byte[][] dictBytes,
|
||||
String[] dictPaths, String langName, UtilCtxt util,
|
||||
JNIUtils jniu, DrawCtx draw, CommonPrefs cp,
|
||||
TransportProcs procs )
|
||||
DrawCtx draw, CommonPrefs cp, TransportProcs procs )
|
||||
{
|
||||
GamePtr gamePtr = initJNI( 0 );
|
||||
game_makeNewGame( gamePtr, gi, dictNames, dictBytes, dictPaths,
|
||||
langName, util, jniu, draw, cp, procs );
|
||||
langName, util, draw, cp, procs );
|
||||
return gamePtr.retain();
|
||||
}
|
||||
|
||||
|
@ -211,7 +216,6 @@ public class XwJNI {
|
|||
String[] dictPaths,
|
||||
String langName,
|
||||
UtilCtxt util,
|
||||
JNIUtils jniu,
|
||||
DrawCtx draw, CommonPrefs cp,
|
||||
TransportProcs procs );
|
||||
|
||||
|
@ -223,7 +227,6 @@ public class XwJNI {
|
|||
String[] dictPaths,
|
||||
String langName,
|
||||
UtilCtxt util,
|
||||
JNIUtils jniu,
|
||||
DrawCtx draw,
|
||||
CommonPrefs cp,
|
||||
TransportProcs procs );
|
||||
|
@ -400,6 +403,19 @@ public class XwJNI {
|
|||
public static native boolean comms_getAddrDisabled( GamePtr gamePtr, CommsConnType typ,
|
||||
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
|
||||
public static class DictWrapper {
|
||||
private int m_dictPtr;
|
||||
|
@ -439,12 +455,10 @@ public class XwJNI {
|
|||
|
||||
public static native boolean dict_tilesAreSame( int dict1, int dict2 );
|
||||
public static native String[] dict_getChars( int dict );
|
||||
public static boolean dict_getInfo( byte[] dict, String name,
|
||||
String path, JNIUtils jniu,
|
||||
public static boolean dict_getInfo( byte[] dict, String name, String path,
|
||||
boolean check, DictInfo info )
|
||||
{
|
||||
return dict_getInfo( getJNI().m_ptr, dict, name, path, jniu,
|
||||
check, info );
|
||||
return dict_getInfo( getJNI().m_ptr, dict, name, path, check, info );
|
||||
}
|
||||
|
||||
public static native int dict_getTileValue( int dictPtr, int tile );
|
||||
|
@ -452,9 +466,9 @@ public class XwJNI {
|
|||
// Dict iterator
|
||||
public final static int MAX_COLS_DICT = 15; // from dictiter.h
|
||||
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,
|
||||
int min, int max );
|
||||
|
@ -469,19 +483,32 @@ public class XwJNI {
|
|||
public static native String dict_iter_getDesc( int closure );
|
||||
|
||||
// Private methods -- called only here
|
||||
private static native int initGlobals();
|
||||
private static native void cleanGlobals( int globals );
|
||||
private static native int initJNI( int jniState, int seed, String tag );
|
||||
private static native int initGlobals(DUtilCtxt dutil, JNIUtils jniu);
|
||||
private static native void cleanGlobals( int jniState );
|
||||
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 dict_ref( int dictPtr );
|
||||
private static native void dict_unref( int dictPtr );
|
||||
private static native boolean dict_getInfo( int jniState, byte[] dict,
|
||||
String name, String path,
|
||||
JNIUtils jniu, boolean check,
|
||||
boolean check,
|
||||
DictInfo info );
|
||||
private static native int dict_iter_init( int jniState, byte[] dict,
|
||||
String name, String path,
|
||||
JNIUtils jniu );
|
||||
String name, String path );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
<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_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_show_gcm">key_show_gcm</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="enable_nfc_toself_title">Enable NFC to self</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_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"
|
||||
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"
|
||||
android:title="@string/enable_sms_toself_title"
|
||||
android:summary="@string/enable_sms_toself_summary"
|
||||
|
|
|
@ -84,6 +84,7 @@ COMMON_SRC_FILES += \
|
|||
$(COMMON_PATH)/movestak.c \
|
||||
$(COMMON_PATH)/dbgutil.c \
|
||||
$(COMMON_PATH)/nli.c \
|
||||
$(COMMON_PATH)/smsproto.c \
|
||||
|
||||
LOCAL_CFLAGS+=$(LOCAL_C_INCLUDES) $(LOCAL_DEFINES) -Wall -std=c99
|
||||
LOCAL_SRC_FILES := $(linux_SRC_FILES) $(LOCAL_SRC_FILES) $(COMMON_SRC_FILES)
|
||||
|
|
|
@ -26,15 +26,16 @@
|
|||
|
||||
typedef struct _JNIState JNIState;
|
||||
|
||||
typedef struct _AndGlobals {
|
||||
typedef struct _AndGameGlobals {
|
||||
VTableMgr* vtMgr;
|
||||
CurGameInfo* gi;
|
||||
DrawCtx* dctx;
|
||||
XW_UtilCtxt* util;
|
||||
struct JNIUtilCtxt* jniutil;
|
||||
TransportProcs* xportProcs;
|
||||
XW_DUtilCtxt* dutil;
|
||||
JNIState* state;
|
||||
} AndGlobals;
|
||||
} AndGameGlobals;
|
||||
|
||||
typedef struct _EnvThreadInfo EnvThreadInfo;
|
||||
|
||||
|
|
|
@ -689,7 +689,7 @@ jEnumToInt( JNIEnv* env, jobject jenum )
|
|||
}
|
||||
|
||||
XWStreamCtxt*
|
||||
and_empty_stream( MPFORMAL AndGlobals* globals )
|
||||
and_empty_stream( MPFORMAL AndGameGlobals* globals )
|
||||
{
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
|
||||
globals, 0, NULL );
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
/* callback for streams */
|
||||
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 {
|
||||
const char* name;
|
||||
|
|
|
@ -30,6 +30,18 @@
|
|||
|
||||
#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 {
|
||||
XWTimerProc proc;
|
||||
void* closure;
|
||||
|
@ -40,21 +52,8 @@ typedef struct _AndUtil {
|
|||
EnvThreadInfo* ti;
|
||||
jobject jutil; /* global ref to object implementing XW_UtilCtxt */
|
||||
TimerStorage timerStorage[NUM_TIMERS_PLUS_ONE];
|
||||
XP_UCHAR* userStrings[N_AND_USER_STRINGS];
|
||||
XP_U32 userStringsBits;
|
||||
#ifdef XWFEATURE_DEVID
|
||||
XP_UCHAR* devIDStorage;
|
||||
#endif
|
||||
} AndUtil;
|
||||
|
||||
|
||||
static VTableMgr*
|
||||
and_util_getVTManager( XW_UtilCtxt* uc )
|
||||
{
|
||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
||||
return globals->vtMgr;
|
||||
}
|
||||
|
||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||
static XWStreamCtxt*
|
||||
and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
||||
|
@ -62,7 +61,7 @@ and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|||
#ifdef DEBUG
|
||||
AndUtil* util = (AndUtil*)uc;
|
||||
#endif
|
||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
||||
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
||||
XWStreamCtxt* stream = and_empty_stream( MPPARM(util->util.mpool)
|
||||
globals );
|
||||
stream_setAddress( stream, channelNo );
|
||||
|
@ -83,6 +82,14 @@ and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|||
__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),
|
||||
XP_U16 boardSize,
|
||||
XP_U16 col, XP_U16 row )
|
||||
|
@ -349,26 +356,24 @@ and_util_altKeyDown( XW_UtilCtxt* uc )
|
|||
return XP_FALSE;
|
||||
}
|
||||
|
||||
|
||||
XP_U32
|
||||
and_util_getCurSeconds( XW_UtilCtxt* uc )
|
||||
and_dutil_getCurSeconds( XW_DUtilCtxt* duc )
|
||||
{
|
||||
AndUtil* andutil = (AndUtil*)uc;
|
||||
XP_U32 curSeconds = getCurSeconds( ENVFORME( andutil->ti ) );
|
||||
AndDUtil* anddutil = (AndDUtil*)duc;
|
||||
XP_U32 curSeconds = getCurSeconds( ENVFORME( anddutil->ti ) );
|
||||
/* struct timeval tv; */
|
||||
/* gettimeofday( &tv, NULL ); */
|
||||
/* XP_LOGF( "%s: %d vs %d", __func__, (int)tv.tv_sec, (int)curSeconds ); */
|
||||
return curSeconds;
|
||||
}
|
||||
|
||||
|
||||
static DictionaryCtxt*
|
||||
and_util_makeEmptyDict( XW_UtilCtxt* uc )
|
||||
{
|
||||
#ifdef STUBBED_DICT
|
||||
XP_ASSERT(0);
|
||||
#else
|
||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
||||
AndGameGlobals* globals = (AndGameGlobals*)uc->closure;
|
||||
AndUtil* andutil = (AndUtil*)uc;
|
||||
DictionaryCtxt* result =
|
||||
and_dictionary_make_empty( MPPARM( ((AndUtil*)uc)->util.mpool )
|
||||
|
@ -378,51 +383,51 @@ and_util_makeEmptyDict( XW_UtilCtxt* uc )
|
|||
}
|
||||
|
||||
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 = "";
|
||||
UTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
|
||||
DUTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
|
||||
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] ) {
|
||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid,
|
||||
if ( ! dutil->userStrings[index] ) {
|
||||
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
||||
stringCode );
|
||||
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 );
|
||||
XP_MEMCPY( buf, jchars, len );
|
||||
buf[len] = '\0';
|
||||
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
||||
deleteLocalRef( env, jresult );
|
||||
util->userStrings[index] = buf;
|
||||
dutil->userStrings[index] = buf;
|
||||
}
|
||||
|
||||
result = util->userStrings[index];
|
||||
UTIL_CBK_TAIL();
|
||||
result = dutil->userStrings[index];
|
||||
DUTIL_CBK_TAIL();
|
||||
return result;
|
||||
}
|
||||
|
||||
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 = "";
|
||||
UTIL_CBK_HEADER("getUserQuantityString", "(II)Ljava/lang/String;" );
|
||||
DUTIL_CBK_HEADER("getUserQuantityString", "(II)Ljava/lang/String;" );
|
||||
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
|
||||
XP_ASSERT( index < VSIZE( util->userStrings ) );
|
||||
XP_ASSERT( index < VSIZE( dutil->userStrings ) );
|
||||
XP_UCHAR** ptrs;
|
||||
|
||||
util->userStringsBits |= 1 << index;
|
||||
ptrs = (XP_UCHAR**)util->userStrings[index];
|
||||
dutil->userStringsBits |= 1 << index;
|
||||
ptrs = (XP_UCHAR**)dutil->userStrings[index];
|
||||
if ( !ptrs ) {
|
||||
ptrs = (XP_UCHAR**)XP_CALLOC( util->util.mpool, MAX_QUANTITY_STRS * sizeof(*ptrs) );
|
||||
util->userStrings[index] = (XP_UCHAR*)ptrs;
|
||||
ptrs = (XP_UCHAR**)XP_CALLOC( dutil->dutil.mpool, MAX_QUANTITY_STRS * sizeof(*ptrs) );
|
||||
dutil->userStrings[index] = (XP_UCHAR*)ptrs;
|
||||
}
|
||||
|
||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid,
|
||||
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid,
|
||||
stringCode, quantity );
|
||||
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
||||
int indx = 0;
|
||||
|
@ -439,7 +444,7 @@ and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quant
|
|||
if ( !ptrs[indx] ) {
|
||||
XP_ASSERT( indx < MAX_QUANTITY_STRS );
|
||||
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 );
|
||||
buf[len] = '\0';
|
||||
ptrs[indx] = buf;
|
||||
|
@ -449,10 +454,83 @@ and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quant
|
|||
deleteLocalRef( env, jresult );
|
||||
|
||||
result = ptrs[indx];
|
||||
UTIL_CBK_TAIL();
|
||||
DUTIL_CBK_TAIL();
|
||||
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
|
||||
and_util_notifyIllegalWords( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
||||
XP_U16 turn, XP_Bool turnLost )
|
||||
|
@ -519,18 +597,18 @@ and_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
|
|||
|
||||
#ifdef XWFEATURE_SMS
|
||||
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 )
|
||||
{
|
||||
XP_Bool same = 0 == strcmp( p1, p2 );
|
||||
if ( !same ) {
|
||||
UTIL_CBK_HEADER( "phoneNumbersSame",
|
||||
DUTIL_CBK_HEADER( "phoneNumbersSame",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Z" );
|
||||
jstring js1 = (*env)->NewStringUTF( env, p1 );
|
||||
jstring js2 = (*env)->NewStringUTF( env, p2 );
|
||||
same = (*env)->CallBooleanMethod( env, util->jutil, mid, js1, js2 );
|
||||
same = (*env)->CallBooleanMethod( env, dutil->jdutil, mid, js1, js2 );
|
||||
deleteLocalRefs( env, js1, js2, DELETE_NO_REF );
|
||||
UTIL_CBK_TAIL();
|
||||
DUTIL_CBK_TAIL();
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
@ -587,50 +665,50 @@ and_util_setIsServer(XW_UtilCtxt* uc, XP_Bool isServer )
|
|||
|
||||
#ifdef XWFEATURE_DEVID
|
||||
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;
|
||||
*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 );
|
||||
jstring jresult = (*env)->CallObjectMethod( env, util->jutil, mid, jbarr );
|
||||
jstring jresult = (*env)->CallObjectMethod( env, dutil->jdutil, mid, jbarr );
|
||||
if ( NULL != jresult ) {
|
||||
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
|
||||
jsize len = (*env)->GetStringUTFLength( env, jresult );
|
||||
if ( NULL != util->devIDStorage
|
||||
&& 0 == XP_MEMCMP( util->devIDStorage, jchars, len ) ) {
|
||||
if ( NULL != dutil->devIDStorage
|
||||
&& 0 == XP_MEMCMP( dutil->devIDStorage, jchars, len ) ) {
|
||||
XP_LOGF( "%s: already have matching devID", __func__ );
|
||||
} else {
|
||||
XP_LOGF( "%s: allocating storage for devID", __func__ );
|
||||
XP_FREEP( util->util.mpool, &util->devIDStorage );
|
||||
util->devIDStorage = XP_MALLOC( util->util.mpool, len + 1 );
|
||||
XP_MEMCPY( util->devIDStorage, jchars, len );
|
||||
util->devIDStorage[len] = '\0';
|
||||
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
||||
dutil->devIDStorage = XP_MALLOC( dutil->dutil.mpool, len + 1 );
|
||||
XP_MEMCPY( dutil->devIDStorage, jchars, len );
|
||||
dutil->devIDStorage[len] = '\0';
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
|
||||
result = (const XP_UCHAR*)util->devIDStorage;
|
||||
result = (const XP_UCHAR*)dutil->devIDStorage;
|
||||
|
||||
jbyte* elems = (*env)->GetByteArrayElements( env, jbarr, NULL );
|
||||
*typ = (DevIDType)elems[0];
|
||||
(*env)->ReleaseByteArrayElements( env, jbarr, elems, 0 );
|
||||
}
|
||||
deleteLocalRef( env, jbarr );
|
||||
UTIL_CBK_TAIL();
|
||||
DUTIL_CBK_TAIL();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
and_util_deviceRegistered( XW_UtilCtxt* uc, DevIDType typ,
|
||||
and_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
|
||||
const XP_UCHAR* idRelay )
|
||||
{
|
||||
UTIL_CBK_HEADER( "deviceRegistered",
|
||||
DUTIL_CBK_HEADER( "deviceRegistered",
|
||||
"(L" PKG_PATH("jni/UtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
|
||||
jstring jstr = (*env)->NewStringUTF( env, idRelay );
|
||||
jobject jtyp = intToJEnum( env, typ,
|
||||
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 );
|
||||
UTIL_CBK_TAIL();
|
||||
DUTIL_CBK_TAIL();
|
||||
}
|
||||
#endif /* XWFEATURE_DEVID */
|
||||
|
||||
|
@ -664,16 +742,23 @@ and_util_engineStopping( XW_UtilCtxt* uc )
|
|||
}
|
||||
#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
|
||||
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;
|
||||
JNIEnv* env = ENVFORME( util->ti );
|
||||
AndGlobals* globals = (AndGlobals*)uc->closure;
|
||||
struct JNIUtilCtxt* jniutil = globals->jniutil;
|
||||
AndDUtil* dutil = (AndDUtil*)duc;
|
||||
JNIEnv* env = ENVFORME( dutil->ti );
|
||||
struct JNIUtilCtxt* jniutil = dutil->jniutil;
|
||||
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 );
|
||||
return result;
|
||||
}
|
||||
|
@ -682,7 +767,7 @@ and_util_md5sum( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len )
|
|||
|
||||
XW_UtilCtxt*
|
||||
makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
||||
AndGlobals* closure )
|
||||
AndGameGlobals* closure )
|
||||
{
|
||||
AndUtil* util = (AndUtil*)XP_CALLOC( mpool, sizeof(*util) );
|
||||
UtilVtable* vtable = (UtilVtable*)XP_CALLOC( mpool, sizeof(*vtable) );
|
||||
|
@ -697,7 +782,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
|||
util->util.gameInfo = gi;
|
||||
|
||||
#define SET_PROC(nam) vtable->m_util_##nam = and_util_##nam
|
||||
SET_PROC(getVTManager);
|
||||
|
||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||
SET_PROC(makeStreamFromAddr);
|
||||
#endif
|
||||
|
@ -725,10 +810,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
|||
SET_PROC(clearTimer);
|
||||
SET_PROC(requestTime);
|
||||
SET_PROC(altKeyDown);
|
||||
SET_PROC(getCurSeconds);
|
||||
SET_PROC(makeEmptyDict);
|
||||
SET_PROC(getUserString);
|
||||
SET_PROC(getUserQuantityString);
|
||||
SET_PROC(notifyIllegalWords);
|
||||
#ifdef XWFEATURE_CHAT
|
||||
SET_PROC(showChat);
|
||||
|
@ -740,10 +822,6 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
|||
SET_PROC(playerScoreHeld);
|
||||
#endif
|
||||
|
||||
#ifdef XWFEATURE_SMS
|
||||
SET_PROC(phoneNumbersSame);
|
||||
#endif
|
||||
|
||||
#ifdef XWFEATURE_BOARDWORDS
|
||||
SET_PROC(cellSquareHeld);
|
||||
#endif
|
||||
|
@ -752,10 +830,6 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
|||
SET_PROC(informMissing);
|
||||
SET_PROC(addrChange);
|
||||
SET_PROC(setIsServer);
|
||||
# ifdef XWFEATURE_DEVID
|
||||
SET_PROC(getDevID);
|
||||
SET_PROC(deviceRegistered);
|
||||
# endif
|
||||
#endif
|
||||
#ifdef XWFEATURE_SEARCHLIMIT
|
||||
SET_PROC(getTraySearchLimits);
|
||||
|
@ -764,9 +838,8 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
|
|||
SET_PROC(engineStarting);
|
||||
SET_PROC(engineStopping);
|
||||
#endif
|
||||
#ifdef COMMS_CHECKSUM
|
||||
SET_PROC(md5sum);
|
||||
#endif
|
||||
|
||||
SET_PROC(getDevUtilCtxt);
|
||||
|
||||
#undef SET_PROC
|
||||
return (XW_UtilCtxt*)util;
|
||||
|
@ -778,31 +851,81 @@ destroyUtil( XW_UtilCtxt** utilc )
|
|||
AndUtil* util = (AndUtil*)*utilc;
|
||||
JNIEnv* env = ENVFORME( util->ti );
|
||||
|
||||
for ( int ii = 0; ii < VSIZE(util->userStrings); ++ii ) {
|
||||
XP_UCHAR* ptr = util->userStrings[ii];
|
||||
if ( NULL != util->jutil ) {
|
||||
(*env)->DeleteGlobalRef( env, util->jutil );
|
||||
}
|
||||
XP_FREE( util->util.mpool, util->util.vtable );
|
||||
XP_FREE( util->util.mpool, util );
|
||||
*utilc = NULL;
|
||||
}
|
||||
|
||||
XW_DUtilCtxt*
|
||||
makeDUtil( MPFORMAL 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 ( 0 == (util->userStringsBits & (1 << ii)) ) {
|
||||
XP_FREE( util->util.mpool, ptr );
|
||||
if ( 0 == (dutil->userStringsBits & (1 << ii)) ) {
|
||||
XP_FREE( dutil->dutil.mpool, ptr );
|
||||
} else {
|
||||
XP_UCHAR** ptrs = (XP_UCHAR**)ptr;
|
||||
for ( int jj = 0; jj < MAX_QUANTITY_STRS; ++jj ) {
|
||||
ptr = ptrs[jj];
|
||||
if ( !!ptr ) {
|
||||
XP_FREE( 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
|
||||
XP_FREEP( util->util.mpool, &util->devIDStorage );
|
||||
XP_FREEP( dutil->dutil.mpool, &dutil->devIDStorage );
|
||||
#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 "util.h"
|
||||
#include "dutil.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,
|
||||
CurGameInfo* gi, AndGlobals* globals );
|
||||
CurGameInfo* gi, AndGameGlobals* globals );
|
||||
void destroyUtil( XW_UtilCtxt** util );
|
||||
|
||||
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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "dictiter.h"
|
||||
#include "dictmgr.h"
|
||||
#include "nli.h"
|
||||
#include "smsproto.h"
|
||||
|
||||
#include "utilwrapper.h"
|
||||
#include "drawwrapper.h"
|
||||
|
@ -60,9 +62,33 @@ struct _EnvThreadInfo {
|
|||
typedef struct _JNIGlobalState {
|
||||
EnvThreadInfo ti;
|
||||
DictMgrCtxt* dictMgr;
|
||||
SMSProto* smsProto;
|
||||
VTableMgr* vtMgr;
|
||||
XW_DUtilCtxt* dutil;
|
||||
JNIUtilCtxt* jniutil;
|
||||
XP_Bool mpoolInUse;
|
||||
MPSLOT
|
||||
} 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_ALL
|
||||
|
||||
|
@ -229,6 +255,7 @@ static void
|
|||
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
|
||||
{
|
||||
if ( jtiles != NULL ) {
|
||||
XP_ASSERT( !!jtiles );
|
||||
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
|
||||
int tmp[MAX_TRAY_TILES];
|
||||
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
|
||||
|
@ -255,33 +282,43 @@ getState( JNIEnv* env, GamePtrType gamePtr )
|
|||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals
|
||||
( JNIEnv* env, jclass C )
|
||||
( JNIEnv* env, jclass C, jobject jdutil, jobject jniu )
|
||||
{
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = mpool_make( NULL );
|
||||
#endif
|
||||
JNIGlobalState* state = (JNIGlobalState*)XP_CALLOC( mpool, sizeof(*state) );
|
||||
map_init( MPPARM(mpool) &state->ti, env );
|
||||
state->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
|
||||
MPASSIGN( state->mpool, mpool );
|
||||
// LOG_RETURNF( "%p", state );
|
||||
return (jint)state;
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)XP_CALLOC( mpool,
|
||||
sizeof(*globalState) );
|
||||
map_init( MPPARM(mpool) &globalState->ti, env );
|
||||
globalState->jniutil = makeJNIUtil( MPPARM(mpool) env, &globalState->ti, jniu );
|
||||
globalState->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||
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
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals
|
||||
( JNIEnv* env, jclass C, jint ptr )
|
||||
( JNIEnv* env, jclass C, jint jniGlobalPtr )
|
||||
{
|
||||
// LOG_FUNC();
|
||||
if ( 0 != ptr ) {
|
||||
JNIGlobalState* state = (JNIGlobalState*)ptr;
|
||||
XP_ASSERT( ENVFORME(&state->ti) == env );
|
||||
dmgr_destroy( state->dictMgr );
|
||||
if ( 0 != jniGlobalPtr ) {
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = state->mpool;
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
map_destroy( &state->ti );
|
||||
XP_FREE( mpool, state );
|
||||
XP_ASSERT( ENVFORME(&globalState->ti) == env );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
@ -511,6 +548,7 @@ loadCommonPrefs( JNIEnv* env, CommonPrefs* cp, jobject j_cp )
|
|||
static XWStreamCtxt*
|
||||
streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream )
|
||||
{
|
||||
XP_ASSERT( !!jstream );
|
||||
int len = (*env)->GetArrayLength( env, jstream );
|
||||
XWStreamCtxt* stream = mem_stream_make_sized( MPPARM(mpool) vtMgr,
|
||||
len, NULL, 0, NULL );
|
||||
|
@ -525,15 +563,15 @@ streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream )
|
|||
****************************************************/
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
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;
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = mpool_make( NULL );
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
|
||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr,
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globalState->vtMgr,
|
||||
NULL, 0, NULL );
|
||||
|
||||
game_saveToStream( NULL, gi, stream, 0 );
|
||||
|
@ -541,24 +579,20 @@ Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream
|
|||
|
||||
result = streamToBArray( env, stream );
|
||||
stream_destroy( stream );
|
||||
|
||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
||||
#ifdef MEM_DEBUG
|
||||
mpool_destroy( mpool );
|
||||
#endif
|
||||
releaseMPool( globalState );
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
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
|
||||
MemPoolCtx* mpool = mpool_make( NULL );
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||
|
||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, vtMgr, jstream );
|
||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
|
||||
globalState->vtMgr, jstream );
|
||||
|
||||
CurGameInfo 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 );
|
||||
|
||||
stream_destroy( stream );
|
||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
||||
#ifdef MEM_DEBUG
|
||||
mpool_destroy( mpool );
|
||||
#endif
|
||||
releaseMPool( globalState );
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
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();
|
||||
jbyteArray result;
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = mpool_make( NULL );
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
|
||||
jbyteArray result;
|
||||
NetLaunchInfo nli = {0};
|
||||
loadNLI( env, &nli, njli );
|
||||
/* CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi ); */
|
||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr,
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globalState->vtMgr,
|
||||
NULL, 0, NULL );
|
||||
|
||||
nli_saveToStream( &nli, stream );
|
||||
|
||||
result = streamToBArray( env, stream );
|
||||
stream_destroy( stream );
|
||||
|
||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
||||
#ifdef MEM_DEBUG
|
||||
mpool_destroy( mpool );
|
||||
#endif
|
||||
releaseMPool( globalState );
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
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();
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = mpool_make( NULL );
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, vtMgr, jstream );
|
||||
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
|
||||
globalState->vtMgr, jstream );
|
||||
|
||||
NetLaunchInfo nli = {0};
|
||||
if ( nli_makeFromStream( &nli, stream ) ) {
|
||||
|
@ -625,10 +654,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_nli_1from_1stream
|
|||
}
|
||||
|
||||
stream_destroy( stream );
|
||||
vtmgr_destroy( MPPARM(mpool) vtMgr );
|
||||
#ifdef MEM_DEBUG
|
||||
mpool_destroy( mpool );
|
||||
#endif
|
||||
releaseMPool( globalState );
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
@ -684,14 +710,18 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1unref
|
|||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
||||
( 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;
|
||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
||||
map_thread( &state->ti, env );
|
||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) env, &state->ti, jniu );
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
||||
jniutil, jname, jDictBytes, jpath,
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
map_thread( &globalState->ti, env );
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = getMPool( globalState );
|
||||
#endif
|
||||
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, globalState->dictMgr,
|
||||
globalState->jniutil, jname, jDictBytes, jpath,
|
||||
NULL, check );
|
||||
if ( NULL != dict ) {
|
||||
if ( NULL != jinfo ) {
|
||||
|
@ -704,8 +734,8 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
|
|||
dict_unref( dict );
|
||||
result = true;
|
||||
}
|
||||
destroyJNIUtil( env, &jniutil );
|
||||
|
||||
releaseMPool( globalState );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -739,10 +769,88 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getTileValue
|
|||
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 {
|
||||
XWGame game;
|
||||
JNIGlobalState* globalJNI;
|
||||
AndGlobals globals;
|
||||
AndGameGlobals globals;
|
||||
// pthread_mutex_t msgMutex;
|
||||
XP_U16 curSaveCount;
|
||||
XP_U16 lastSavedSize;
|
||||
|
@ -761,27 +869,26 @@ struct _JNIState {
|
|||
|
||||
#define XWJNI_START_GLOBALS() \
|
||||
XWJNI_START() \
|
||||
AndGlobals* globals = &state->globals; \
|
||||
AndGameGlobals* globals = &state->globals; \
|
||||
|
||||
#define XWJNI_END() \
|
||||
} \
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
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? */
|
||||
/* struct timeval tv; */
|
||||
/* gettimeofday( &tv, NULL ); */
|
||||
/* srandom( tv.tv_sec ); */
|
||||
#ifdef MEM_DEBUG
|
||||
const char* tag = (*env)->GetStringUTFChars( env, jtag, NULL );
|
||||
MemPoolCtx* mpool = mpool_make( tag );
|
||||
(*env)->ReleaseStringUTFChars( env, jtag, tag );
|
||||
MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool;
|
||||
#endif
|
||||
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
||||
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
||||
AndGlobals* globals = &state->globals;
|
||||
AndGameGlobals* globals = &state->globals;
|
||||
globals->dutil = state->globalJNI->dutil;
|
||||
globals->state = (JNIState*)state;
|
||||
MPASSIGN( state->mpool, mpool );
|
||||
globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool));
|
||||
|
@ -807,8 +914,8 @@ JNIEXPORT void JNICALL
|
|||
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
||||
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi,
|
||||
jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths,
|
||||
jstring j_lang, jobject j_util, jobject jniu, jobject j_draw,
|
||||
jobject j_cp, jobject j_procs )
|
||||
jstring j_lang, jobject j_util, jobject j_draw, jobject j_cp,
|
||||
jobject j_procs )
|
||||
{
|
||||
XWJNI_START_GLOBALS();
|
||||
EnvThreadInfo* ti = &state->globalJNI->ti;
|
||||
|
@ -816,7 +923,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
|
|||
globals->gi = gi;
|
||||
globals->util = makeUtil( MPPARM(mpool) ti, j_util, gi,
|
||||
globals );
|
||||
globals->jniutil = makeJNIUtil( MPPARM(mpool) env, ti, jniu );
|
||||
globals->jniutil = state->globalJNI->jniutil;
|
||||
DrawCtx* dctx = NULL;
|
||||
if ( !!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
|
||||
MemPoolCtx* mpool = state->mpool;
|
||||
#endif
|
||||
AndGlobals* globals = &state->globals;
|
||||
AndGameGlobals* globals = &state->globals;
|
||||
|
||||
destroyGI( MPPARM(mpool) &globals->gi );
|
||||
|
||||
|
@ -867,22 +974,19 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
|
|||
destroyDraw( &globals->dctx );
|
||||
destroyXportProcs( &globals->xportProcs );
|
||||
destroyUtil( &globals->util );
|
||||
destroyJNIUtil( env, &globals->jniutil );
|
||||
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
||||
|
||||
MAP_REMOVE( &state->globalJNI->ti, env );
|
||||
/* pthread_mutex_destroy( &state->msgMutex ); */
|
||||
|
||||
XP_FREE( mpool, state );
|
||||
mpool_destroy( mpool );
|
||||
} /* game_dispose */
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
||||
( JNIEnv* env, jclass C, GamePtrType gamePtr, jbyteArray jstream, jobject /*out*/jgi,
|
||||
jobjectArray jdictNames, jobjectArray jdicts, jobjectArray jpaths,
|
||||
jstring jlang, jobject jutil, jobject jniu, jobject jdraw, jobject jcp,
|
||||
jobject jprocs )
|
||||
jstring jlang, jobject jutil, jobject jdraw, jobject jcp, jobject jprocs )
|
||||
{
|
||||
jboolean result;
|
||||
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->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,
|
||||
globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths,
|
||||
jlang );
|
||||
|
@ -923,7 +1027,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
|
|||
destroyDraw( &globals->dctx );
|
||||
destroyXportProcs( &globals->xportProcs );
|
||||
destroyUtil( &globals->util );
|
||||
destroyJNIUtil( env, &globals->jniutil );
|
||||
destroyGI( MPPARM(mpool) &globals->gi );
|
||||
}
|
||||
|
||||
|
@ -1556,7 +1659,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores
|
|||
void
|
||||
and_send_on_close( XWStreamCtxt* stream, void* closure )
|
||||
{
|
||||
AndGlobals* globals = (AndGlobals*)closure;
|
||||
AndGameGlobals* globals = (AndGameGlobals*)closure;
|
||||
JNIState* state = (JNIState*)globals->state;
|
||||
|
||||
XP_ASSERT( !!state->game.comms );
|
||||
|
@ -2049,8 +2152,8 @@ JNIEXPORT jboolean JNICALL
|
|||
Java_org_eehouse_android_xw4_jni_XwJNI_haveEnv
|
||||
( JNIEnv* env, jclass C, jint jniGlobalPtr )
|
||||
{
|
||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
||||
jboolean result = NULL != prvEnvForMe(&state->ti);
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
jboolean result = NULL != prvEnvForMe(&globalState->ti);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2102,29 +2205,25 @@ static void freeIndices( DictIterData* data );
|
|||
JNIEXPORT jint JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
|
||||
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, jstring jname,
|
||||
jstring jpath, jobject jniu )
|
||||
jstring jpath )
|
||||
{
|
||||
jint closure = 0;
|
||||
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
|
||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||
|
||||
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(state->mpool) env,
|
||||
&state->ti, jniu );
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
|
||||
jniutil, jname, jDictBytes, jpath, NULL,
|
||||
false );
|
||||
DictionaryCtxt* dict = makeDict( MPPARM(globalState->mpool) env,
|
||||
globalState->dictMgr, globalState->jniutil,
|
||||
jname, jDictBytes, jpath, NULL, false );
|
||||
if ( !!dict ) {
|
||||
DictIterData* data = XP_CALLOC( state->mpool, sizeof(*data) );
|
||||
DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
|
||||
data->env = env;
|
||||
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(state->mpool) );
|
||||
data->jniutil = jniutil;
|
||||
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globalState->mpool) );
|
||||
data->jniutil = globalState->jniutil;
|
||||
data->dict = dict;
|
||||
data->depth = 2;
|
||||
#ifdef MEM_DEBUG
|
||||
data->mpool = state->mpool;
|
||||
data->mpool = globalState->mpool;
|
||||
#endif
|
||||
closure = (int)data;
|
||||
} else {
|
||||
destroyJNIUtil( env, &jniutil );
|
||||
}
|
||||
return closure;
|
||||
}
|
||||
|
@ -2152,7 +2251,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy
|
|||
#endif
|
||||
|
||||
dict_unref( data->dict );
|
||||
destroyJNIUtil( env, &data->jniutil );
|
||||
freeIndices( data );
|
||||
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
|
||||
XP_FREE( mpool, data );
|
||||
|
|
|
@ -53,7 +53,7 @@ git checkout ${TAG}${BRANCH}
|
|||
cd ./xwords4/android/
|
||||
./scripts/arelease.sh --apk-list $OUT_FILE
|
||||
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
|
||||
cat $OUT_FILE | while read APK; do
|
||||
|
|
|
@ -54,7 +54,7 @@ fi
|
|||
|
||||
if [ -z "$FILES" ]; then
|
||||
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
|
||||
done
|
||||
fi
|
||||
|
|
|
@ -166,6 +166,7 @@ board_make( MPFORMAL ModelCtxt* model, ServerCtxt* server, DrawCtx* draw,
|
|||
|
||||
result->draw = draw;
|
||||
result->util = util;
|
||||
result->dutil = util_getDevUtilCtxt( util );
|
||||
result->gi = util->gameInfo;
|
||||
XP_ASSERT( !!result->gi );
|
||||
|
||||
|
@ -1090,11 +1091,10 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
|
|||
XP_MEMSET( &bwl, 0, sizeof(bwl) );
|
||||
|
||||
if ( !legal ) {
|
||||
stream = mem_stream_make( MPPARM(board->mpool)
|
||||
util_getVTManager(board->util), NULL,
|
||||
CHANNEL_NONE, (MemStreamCloseCallback)NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(board->mpool)
|
||||
dutil_getVTManager(board->dutil) );
|
||||
|
||||
const XP_UCHAR* str = util_getUserString(board->util,
|
||||
const XP_UCHAR* str = dutil_getUserString( board->dutil,
|
||||
STR_COMMIT_CONFIRM );
|
||||
stream_catString( stream, str );
|
||||
|
||||
|
@ -1319,10 +1319,8 @@ timerFiredForPen( BoardCtxt* board )
|
|||
NULL, NULL, NULL );
|
||||
if ( listWords ) {
|
||||
XWStreamCtxt* stream =
|
||||
mem_stream_make( MPPARM(board->mpool)
|
||||
util_getVTManager(board->util), NULL,
|
||||
CHANNEL_NONE,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
mem_stream_make_raw( MPPARM(board->mpool)
|
||||
dutil_getVTManager(board->dutil) );
|
||||
model_listWordsThrough( board->model, modelCol, modelRow,
|
||||
stream );
|
||||
util_cellSquareHeld( board->util, stream );
|
||||
|
@ -1428,7 +1426,7 @@ board_pushTimerSave( BoardCtxt* board )
|
|||
{
|
||||
if ( board->gi->timerEnabled ) {
|
||||
if ( board->timerSaveCount++ == 0 ) {
|
||||
board->timerStoppedTime = util_getCurSeconds( board->util );
|
||||
board->timerStoppedTime = dutil_getCurSeconds( board->dutil );
|
||||
#ifdef DEBUG
|
||||
board->timerStoppedTurn = server_getCurrentTurn( board->server,
|
||||
NULL );
|
||||
|
@ -1451,7 +1449,7 @@ board_popTimerSave( BoardCtxt* board )
|
|||
XP_ASSERT( board->timerStoppedTurn == turn );
|
||||
|
||||
if ( --board->timerSaveCount == 0 && turn >= 0 ) {
|
||||
XP_U32 curTime = util_getCurSeconds( board->util );
|
||||
XP_U32 curTime = dutil_getCurSeconds( board->dutil );
|
||||
XP_U32 elapsed;
|
||||
|
||||
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 )
|
||||
{
|
||||
XP_Bool success = XP_TRUE;
|
||||
XP_Rect cellRect;
|
||||
XP_Rect cellRect = {0};
|
||||
Tile tile;
|
||||
XP_Bool isBlank, isEmpty, recent, pending = XP_FALSE;
|
||||
XWBonusType bonus;
|
||||
|
|
|
@ -139,6 +139,7 @@ struct BoardCtxt {
|
|||
ModelCtxt* model;
|
||||
ServerCtxt* server;
|
||||
DrawCtx* draw;
|
||||
XW_DUtilCtxt* dutil;
|
||||
XW_UtilCtxt* util;
|
||||
|
||||
struct CurGameInfo* gi;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "comms.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "dutil.h"
|
||||
#include "game.h"
|
||||
#include "xwstream.h"
|
||||
#include "memstream.h"
|
||||
|
@ -100,6 +101,7 @@ typedef struct AddressRecord {
|
|||
|
||||
struct CommsCtxt {
|
||||
XW_UtilCtxt* util;
|
||||
XW_DUtilCtxt* dutil;
|
||||
|
||||
XP_U32 connID; /* set from gameID: 0 means ignore; otherwise
|
||||
must match. Set by server. */
|
||||
|
@ -353,8 +355,7 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
|
|||
#endif
|
||||
)
|
||||
{
|
||||
CommsCtxt* comms = (CommsCtxt*)XP_MALLOC( mpool, sizeof(*comms) );
|
||||
XP_MEMSET( comms, 0, sizeof(*comms) );
|
||||
CommsCtxt* comms = (CommsCtxt*)XP_CALLOC( mpool, sizeof(*comms) );
|
||||
#ifdef DEBUG
|
||||
comms->tag = mpool_getTag(mpool);
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
comms->dutil = util_getDevUtilCtxt( util );
|
||||
comms->util = util;
|
||||
comms->dutil = util_getDevUtilCtxt( util );
|
||||
|
||||
#ifdef XWFEATURE_RELAY
|
||||
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 );
|
||||
stream_getBytes( stream, msg->msg, msg->len );
|
||||
#ifdef COMMS_CHECKSUM
|
||||
msg->checksum = util_md5sum( comms->util, msg->msg, msg->len );
|
||||
msg->checksum = dutil_md5sum( comms->dutil, msg->msg, msg->len );
|
||||
#endif
|
||||
msg->next = (MsgQueueElem*)NULL;
|
||||
*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)comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
|
||||
break;
|
||||
#endif
|
||||
#if defined XWFEATURE_SMS
|
||||
case COMMS_CONN_SMS:
|
||||
(void)comms_resendAll( comms, COMMS_CONN_NONE, XP_FALSE );
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
@ -973,7 +981,6 @@ comms_setAddr( CommsCtxt* comms, const CommsAddrRec* addr )
|
|||
setDoHeartbeat( comms );
|
||||
#endif
|
||||
sendConnect( comms, XP_TRUE );
|
||||
|
||||
} /* comms_setAddr */
|
||||
|
||||
void
|
||||
|
@ -1136,10 +1143,8 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
|||
newMsgElem->sendCount = 0;
|
||||
#endif
|
||||
|
||||
hdrStream = mem_stream_make( MPPARM(comms->mpool)
|
||||
util_getVTManager(comms->util),
|
||||
NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
hdrStream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||
dutil_getVTManager(comms->dutil));
|
||||
stream_open( hdrStream );
|
||||
#if 0 < COMMS_VERSION
|
||||
stream_putU16( hdrStream, HAS_VERSION_FLAG );
|
||||
|
@ -1168,7 +1173,7 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
|||
}
|
||||
|
||||
#ifdef COMMS_CHECKSUM
|
||||
newMsgElem->checksum = util_md5sum( comms->util, newMsgElem->msg,
|
||||
newMsgElem->checksum = dutil_md5sum( comms->dutil, newMsgElem->msg,
|
||||
newMsgElem->len );
|
||||
#endif
|
||||
return newMsgElem;
|
||||
|
@ -1508,7 +1513,7 @@ comms_resendAll( CommsCtxt* comms, CommsConnType filter, XP_Bool force )
|
|||
XP_Bool success = XP_TRUE;
|
||||
XP_ASSERT( !!comms );
|
||||
|
||||
XP_U32 now = util_getCurSeconds( comms->util );
|
||||
XP_U32 now = dutil_getCurSeconds( comms->dutil );
|
||||
if ( !force && (now < comms->nextResend) ) {
|
||||
XP_LOGF( "%s: aborting: %d seconds left in backoff", __func__,
|
||||
comms->nextResend - now );
|
||||
|
@ -1664,7 +1669,7 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
|
|||
}
|
||||
if ( ID_TYPE_NONE == typ /* error case */
|
||||
|| '\0' != devID[0] ) /* new info case */ {
|
||||
util_deviceRegistered( comms->util, typ, devID );
|
||||
dutil_deviceRegistered( comms->dutil, typ, devID );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1845,7 +1850,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
|||
static void
|
||||
noteHBReceived( CommsCtxt* comms/* , const CommsAddrRec* addr */ )
|
||||
{
|
||||
comms->lastMsgRcvdTime = util_getCurSeconds( comms->util );
|
||||
comms->lastMsgRcvdTime = dutil_getCurSeconds( comms->dutil );
|
||||
setHeartbeatTimer( comms );
|
||||
}
|
||||
#else
|
||||
|
@ -1976,12 +1981,15 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
|
|||
break;
|
||||
case COMMS_CONN_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 )
|
||||
&& addr->u.sms.port == rec->addr.u.sms.port ) {
|
||||
matched = XP_TRUE;
|
||||
XP_ASSERT( 0 );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case COMMS_CONN_NONE:
|
||||
|
@ -2227,7 +2235,7 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
|||
XP_U16 len = stream_getSize( stream );
|
||||
// stream_getPtr pts at base, but sum excludes relay header
|
||||
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",
|
||||
TAGPRMS, len, sum );
|
||||
XP_FREE( comms->mpool, sum );
|
||||
|
@ -2433,7 +2441,7 @@ heartbeat_checks( CommsCtxt* comms )
|
|||
|
||||
do {
|
||||
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);
|
||||
if ( comms->lastMsgRcvdTime < tooLongAgo ) {
|
||||
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 ) {
|
||||
char buf[128];
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(comms->mpool)
|
||||
util_getVTManager(comms->util),
|
||||
NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||
dutil_getVTManager(comms->dutil));
|
||||
snprintf( buf, sizeof(buf), TAGFMT() "called on %p from %s:\n", TAGPRMS,
|
||||
addr, caller );
|
||||
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 );
|
||||
XWStreamCtxt* stream;
|
||||
stream = mem_stream_make( MPPARM(comms->mpool)
|
||||
util_getVTManager(comms->util),
|
||||
NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(comms->mpool)
|
||||
dutil_getVTManager(comms->dutil) );
|
||||
if ( stream != NULL ) {
|
||||
CommsAddrRec addr;
|
||||
stream_open( stream );
|
||||
|
@ -3200,7 +3204,7 @@ putDevID( const CommsCtxt* comms, XWStreamCtxt* stream )
|
|||
{
|
||||
# if XWRELAY_PROTO_VERSION >= XWRELAY_PROTO_VERSION_CLIENTID
|
||||
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 );
|
||||
stream_putU8( stream, typ );
|
||||
if ( ID_TYPE_NONE != typ ) {
|
||||
|
|
|
@ -129,6 +129,22 @@ typedef enum {
|
|||
OBJ_TRAY
|
||||
} 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 */
|
||||
typedef struct BoardCtxt BoardCtxt;
|
||||
typedef struct CommMgrCtxt CommMgrCtxt;
|
||||
|
@ -144,6 +160,7 @@ typedef struct XWStreamCtxt XWStreamCtxt;
|
|||
typedef struct TrayContext TrayContext;
|
||||
typedef struct PoolContext PoolContext;
|
||||
typedef struct XW_UtilCtxt XW_UtilCtxt;
|
||||
typedef struct XW_DUtilCtxt XW_DUtilCtxt;
|
||||
|
||||
/* 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. */
|
||||
|
@ -191,6 +208,8 @@ typedef enum {
|
|||
BONUS_LAST
|
||||
} XWBonusType;
|
||||
|
||||
#define PERSIST_KEY(str) __FILE__ ":" str
|
||||
|
||||
/* 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
|
||||
* want to deal with versioning in the common code. Second, becuase they
|
||||
|
|
|
@ -48,6 +48,7 @@ COMMONSRC = \
|
|||
$(COMMONDIR)/vtabmgr.c \
|
||||
$(COMMONDIR)/dictmgr.c \
|
||||
$(COMMONDIR)/dbgutil.c \
|
||||
$(COMMONDIR)/smsproto.c \
|
||||
|
||||
# PENDING: define this in terms of above!!!
|
||||
|
||||
|
@ -85,5 +86,6 @@ COMMON5 = \
|
|||
$(COMMONOBJDIR)/vtabmgr.o \
|
||||
$(COMMONOBJDIR)/dictmgr.o \
|
||||
$(COMMONOBJDIR)/dbgutil.o \
|
||||
$(COMMONOBJDIR)/smsproto.o \
|
||||
|
||||
COMMONOBJ = $(COMMON1) $(COMMON2) $(COMMON3) $(COMMON4) $(COMMON5)
|
||||
|
|
|
@ -44,8 +44,6 @@ extern "C" {
|
|||
#define DICT_HEADER_MASK 0x08
|
||||
#define DICT_SYNONYMS_MASK 0x10
|
||||
|
||||
typedef XP_U8 XP_LangCode;
|
||||
|
||||
typedef enum {
|
||||
INTRADE_MW_TEXT = BONUS_LAST
|
||||
} 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;
|
||||
assertUtilOK( util );
|
||||
while ( 0 == gameID ) {
|
||||
gameID = util_getCurSeconds( util );
|
||||
gameID = dutil_getCurSeconds( util_getDevUtilCtxt( util ) );
|
||||
}
|
||||
return gameID;
|
||||
}
|
||||
|
|
|
@ -258,6 +258,11 @@ void*
|
|||
mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
|
||||
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 );
|
||||
|
||||
if ( !entry ) {
|
||||
|
@ -271,7 +276,9 @@ mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
|
|||
entry->lineNo = lineNo;
|
||||
entry->size = newsize;
|
||||
}
|
||||
return entry->ptr;
|
||||
result = entry->ptr;
|
||||
}
|
||||
return result;
|
||||
} /* mpool_realloc */
|
||||
|
||||
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
|
||||
* 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*
|
||||
mem_stream_make( MPFORMAL VTableMgr* vtmgr, void* closure,
|
||||
XP_PlayerAddr channelNo, MemStreamCloseCallback onClose )
|
||||
|
|
|
@ -32,6 +32,8 @@ extern "C" {
|
|||
typedef void (*MemStreamCloseCallback)( XWStreamCtxt* stream,
|
||||
void* closure );
|
||||
|
||||
XWStreamCtxt* mem_stream_make_raw( MPFORMAL VTableMgr* vtmgr);
|
||||
|
||||
XWStreamCtxt* mem_stream_make( MPFORMAL VTableMgr* vtmgr,
|
||||
void* closure,
|
||||
XP_PlayerAddr addr, /* should be in a
|
||||
|
|
|
@ -108,6 +108,7 @@ model_make( MPFORMAL DictionaryCtxt* dict, const PlayerDicts* dicts,
|
|||
MPASSIGN(result->vol.mpool, mpool);
|
||||
|
||||
result->vol.util = util;
|
||||
result->vol.dutil = util_getDevUtilCtxt( util );
|
||||
result->vol.wni.proc = recordWord;
|
||||
result->vol.wni.closure = &result->vol.rwi;
|
||||
|
||||
|
@ -295,7 +296,7 @@ model_setSize( ModelCtxt* model, XP_U16 nCols )
|
|||
stack_init( model->vol.stack );
|
||||
} else {
|
||||
model->vol.stack = stack_make( MPPARM(model->vol.mpool)
|
||||
util_getVTManager(model->vol.util));
|
||||
dutil_getVTManager(model->vol.dutil));
|
||||
}
|
||||
} /* model_setSize */
|
||||
|
||||
|
@ -2090,13 +2091,13 @@ printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry
|
|||
}
|
||||
|
||||
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 );
|
||||
} else {
|
||||
if ( isHorizontal ) {
|
||||
format = util_getUserString( model->vol.util, STRS_MOVE_ACROSS );
|
||||
format = dutil_getUserString( model->vol.dutil, STRS_MOVE_ACROSS );
|
||||
} else {
|
||||
format = util_getUserString( model->vol.util, STRS_MOVE_DOWN );
|
||||
format = dutil_getUserString( model->vol.dutil, STRS_MOVE_DOWN );
|
||||
}
|
||||
|
||||
row = mi->commonCoord;
|
||||
|
@ -2114,7 +2115,7 @@ printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry
|
|||
}
|
||||
|
||||
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 ),
|
||||
closure->dict, (XP_UCHAR*)traybuf, sizeof(traybuf),
|
||||
XP_FALSE );
|
||||
|
@ -2149,17 +2150,17 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
|
|||
formatTray( (const TrayTileSet*) &entry->u.trade.newTiles,
|
||||
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 );
|
||||
printString( stream, buf );
|
||||
printString( stream, (XP_UCHAR*)XP_CR );
|
||||
break;
|
||||
|
||||
case PHONY_TYPE:
|
||||
format = util_getUserString( model->vol.util, STR_PHONY_REJECTED );
|
||||
format = dutil_getUserString( model->vol.dutil, STR_PHONY_REJECTED );
|
||||
printString( stream, format );
|
||||
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 );
|
||||
printString( stream, buf );
|
||||
|
||||
|
@ -2174,7 +2175,7 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
|
|||
if ( entry->moveType == PHONY_TYPE ) {
|
||||
/* printString( stream, (XP_UCHAR*)"phony rejected " ); */
|
||||
} 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,
|
||||
formatTray( &entry->u.move.newTiles, dict,
|
||||
traybuf1, sizeof(traybuf1),
|
||||
|
@ -2195,9 +2196,9 @@ static void
|
|||
copyStack( const ModelCtxt* model, StackCtxt* destStack,
|
||||
const StackCtxt* srcStack )
|
||||
{
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(model->vol.mpool)
|
||||
util_getVTManager(model->vol.util),
|
||||
NULL, 0, NULL );
|
||||
XWStreamCtxt* stream =
|
||||
mem_stream_make_raw( MPPARM(model->vol.mpool)
|
||||
dutil_getVTManager(model->vol.dutil) );
|
||||
|
||||
stack_writeToStream( (StackCtxt*)srcStack, stream );
|
||||
stack_loadFromStream( destStack, stream );
|
||||
|
|
|
@ -51,6 +51,7 @@ typedef struct _RecordWordsInfo {
|
|||
} RecordWordsInfo;
|
||||
|
||||
typedef struct ModelVolatiles {
|
||||
XW_DUtilCtxt* dutil;
|
||||
XW_UtilCtxt* util;
|
||||
struct CurGameInfo* gi;
|
||||
DictionaryCtxt* dict;
|
||||
|
|
|
@ -120,9 +120,7 @@ stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream )
|
|||
stack->highWaterMark = stream_getU16( stream );
|
||||
stack->nEntries = stream_getU16( stream );
|
||||
stack->top = stream_getU32( stream );
|
||||
stack->data = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr,
|
||||
NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
stack->data = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
|
||||
|
||||
stream_getFromStream( stack->data, stream, nBytes );
|
||||
} else {
|
||||
|
@ -164,8 +162,8 @@ StackCtxt*
|
|||
stack_copy( const StackCtxt* stack )
|
||||
{
|
||||
StackCtxt* newStack = NULL;
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(stack->mpool)
|
||||
stack->vtmgr, NULL, 0, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(stack->mpool)
|
||||
stack->vtmgr );
|
||||
stack_writeToStream( stack, stream );
|
||||
|
||||
newStack = stack_make( MPPARM(stack->mpool) stack->vtmgr );
|
||||
|
@ -184,8 +182,7 @@ pushEntryImpl( StackCtxt* stack, const StackEntry* entry )
|
|||
XWStreamCtxt* stream = stack->data;
|
||||
|
||||
if ( !stream ) {
|
||||
stream = mem_stream_make( MPPARM(stack->mpool) stack->vtmgr, NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
|
||||
stack->data = stream;
|
||||
}
|
||||
|
||||
|
|
|
@ -513,7 +513,7 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
|
|||
score += EMPTIED_TRAY_BONUS;
|
||||
|
||||
if ( !!stream ) {
|
||||
const XP_UCHAR* bstr = util_getUserString( model->vol.util,
|
||||
const XP_UCHAR* bstr = dutil_getUserString( model->vol.dutil,
|
||||
STR_BONUS_ALL );
|
||||
stream_catString( stream, bstr );
|
||||
}
|
||||
|
@ -845,7 +845,7 @@ formatSummary( XWStreamCtxt* stream, const ModelCtxt* model, XP_U16 score )
|
|||
{
|
||||
XP_UCHAR buf[60];
|
||||
XP_SNPRINTF( buf, sizeof(buf),
|
||||
util_getUserString(model->vol.util, STRD_TURN_SCORE),
|
||||
dutil_getUserString(model->vol.dutil, STRD_TURN_SCORE),
|
||||
score );
|
||||
XP_ASSERT( XP_STRLEN(buf) < sizeof(buf) );
|
||||
stream_catString( stream, buf );
|
||||
|
|
|
@ -531,7 +531,7 @@ setRoleStrings( NewGameCtx* ngc )
|
|||
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 );
|
||||
} /* setRoleStrings */
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ typedef struct ServerVolatiles {
|
|||
ModelCtxt* model;
|
||||
CommsCtxt* comms;
|
||||
XW_UtilCtxt* util;
|
||||
XW_DUtilCtxt* dutil;
|
||||
CurGameInfo* gi;
|
||||
TurnChangeListener turnChangeListener;
|
||||
void* turnChangeData;
|
||||
|
@ -271,6 +272,7 @@ server_make( MPFORMAL ModelCtxt* model, CommsCtxt* comms, XW_UtilCtxt* util )
|
|||
result->vol.model = model;
|
||||
result->vol.comms = comms;
|
||||
result->vol.util = util;
|
||||
result->vol.dutil = util_getDevUtilCtxt( util );
|
||||
result->vol.gi = util->gameInfo;
|
||||
|
||||
initServer( result );
|
||||
|
@ -672,7 +674,7 @@ sendChatToClientsExcept( ServerCtxt* server, XP_U16 skip, const XP_UCHAR* msg,
|
|||
void
|
||||
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 ) {
|
||||
sendChatTo( server, SERVER_DEVICE, msg, from, timestamp );
|
||||
} else {
|
||||
|
@ -828,10 +830,8 @@ static XWStreamCtxt*
|
|||
mkServerStream( ServerCtxt* server )
|
||||
{
|
||||
XWStreamCtxt* stream;
|
||||
stream = mem_stream_make( MPPARM(server->mpool)
|
||||
util_getVTManager(server->vol.util),
|
||||
NULL, CHANNEL_NONE,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(server->mpool)
|
||||
dutil_getVTManager(server->vol.dutil) );
|
||||
XP_ASSERT( !!stream );
|
||||
return stream;
|
||||
} /* mkServerStream */
|
||||
|
@ -849,11 +849,11 @@ makeRobotMove( ServerCtxt* server )
|
|||
XP_Bool timerEnabled = gi->timerEnabled;
|
||||
XP_Bool canMove;
|
||||
XP_U32 time = 0L; /* stupid compiler.... */
|
||||
XW_UtilCtxt* util = server->vol.util;
|
||||
XW_DUtilCtxt* dutil = server->vol.dutil;
|
||||
XP_Bool forceTrade = XP_FALSE;
|
||||
|
||||
if ( timerEnabled ) {
|
||||
time = util_getCurSeconds( util );
|
||||
time = dutil_getCurSeconds( dutil );
|
||||
}
|
||||
|
||||
#ifdef XWFEATURE_SLOW_ROBOT
|
||||
|
@ -917,7 +917,7 @@ makeRobotMove( ServerCtxt* server )
|
|||
|
||||
if ( !!stream ) {
|
||||
XP_UCHAR buf[64];
|
||||
str = util_getUserQuantityString( util, STRD_ROBOT_TRADED,
|
||||
str = dutil_getUserQuantityString( dutil, STRD_ROBOT_TRADED,
|
||||
MAX_TRAY_TILES );
|
||||
XP_SNPRINTF( buf, sizeof(buf), str, MAX_TRAY_TILES );
|
||||
|
||||
|
@ -951,7 +951,7 @@ makeRobotMove( ServerCtxt* server )
|
|||
|
||||
if ( timerEnabled ) {
|
||||
gi->players[turn].secondsUsed +=
|
||||
(XP_U16)(util_getCurSeconds( util ) - time);
|
||||
(XP_U16)(dutil_getCurSeconds( dutil ) - time);
|
||||
} else {
|
||||
XP_ASSERT( gi->players[turn].secondsUsed == 0 );
|
||||
}
|
||||
|
@ -1013,6 +1013,7 @@ showPrevScore( ServerCtxt* server )
|
|||
{
|
||||
if ( server->nv.showRobotScores ) { /* this can be changed between turns */
|
||||
XW_UtilCtxt* util = server->vol.util;
|
||||
XW_DUtilCtxt* dutil = server->vol.dutil;
|
||||
XWStreamCtxt* stream;
|
||||
const XP_UCHAR* str;
|
||||
XP_UCHAR buf[128];
|
||||
|
@ -1025,9 +1026,9 @@ showPrevScore( ServerCtxt* server )
|
|||
lp = &gi->players[prevTurn];
|
||||
|
||||
if ( LP_IS_LOCAL(lp) ) {
|
||||
str = util_getUserString( util, STR_ROBOT_MOVED );
|
||||
str = dutil_getUserString( dutil, STR_ROBOT_MOVED );
|
||||
} else {
|
||||
str = util_getUserString( util, STRS_REMOTE_MOVED );
|
||||
str = dutil_getUserString( dutil, STRS_REMOTE_MOVED );
|
||||
}
|
||||
XP_SNPRINTF( buf, sizeof(buf), str, lp->name );
|
||||
str = buf;
|
||||
|
@ -2243,7 +2244,7 @@ makeTradeReportIf( ServerCtxt* server, const TrayTileSet* tradedTiles )
|
|||
if ( server->nv.showRobotScores ) {
|
||||
XP_UCHAR tradeBuf[64];
|
||||
const XP_UCHAR* tradeStr =
|
||||
util_getUserQuantityString( server->vol.util, STRD_ROBOT_TRADED,
|
||||
dutil_getUserQuantityString( server->vol.dutil, STRD_ROBOT_TRADED,
|
||||
tradedTiles->nTiles );
|
||||
XP_SNPRINTF( tradeBuf, sizeof(tradeBuf), tradeStr,
|
||||
tradedTiles->nTiles );
|
||||
|
@ -2504,6 +2505,9 @@ server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
|
|||
nextTurn( server, PICK_NEXT );
|
||||
}
|
||||
|
||||
XP_LOGF( "%s(): player %d now has %d tiles", __func__, turn,
|
||||
model_getNumTilesInTray( model, turn ) );
|
||||
|
||||
return XP_TRUE;
|
||||
} /* server_commitMove */
|
||||
|
||||
|
@ -2706,7 +2710,7 @@ setTurn( ServerCtxt* server, XP_S16 turn )
|
|||
|| (!amServer(server) || (0 == server->nv.pendingRegistrations)));
|
||||
if ( server->nv.currentTurn != turn || 1 == server->vol.gi->nPlayers ) {
|
||||
server->nv.currentTurn = turn;
|
||||
server->nv.lastMoveTime = util_getCurSeconds( server->vol.util );
|
||||
server->nv.lastMoveTime = dutil_getCurSeconds( server->vol.dutil );
|
||||
callTurnChangeListener( server );
|
||||
}
|
||||
}
|
||||
|
@ -3025,7 +3029,7 @@ server_formatDictCounts( ServerCtxt* server, XWStreamCtxt* stream,
|
|||
Tile tile;
|
||||
XP_U16 nChars, nPrinted;
|
||||
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 );
|
||||
const XP_UCHAR* langName;
|
||||
|
||||
|
@ -3096,7 +3100,7 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
|
|||
|
||||
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,
|
||||
nLeft );
|
||||
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
||||
|
@ -3136,7 +3140,7 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
|
|||
XP_ASSERT( offset < sizeof(cntsBuf) );
|
||||
}
|
||||
|
||||
fmt = util_getUserQuantityString( server->vol.util, STRD_REMAINS_EXPL,
|
||||
fmt = dutil_getUserQuantityString( server->vol.dutil, STRD_REMAINS_EXPL,
|
||||
nLeft );
|
||||
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
|
||||
stream_catString( stream, buf );
|
||||
|
@ -3226,9 +3230,9 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
XP_S16 quitter = server->nv.quitter;
|
||||
XP_Bool quitterDone = XP_FALSE;
|
||||
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 );
|
||||
const XP_UCHAR* subString = util_getUserString( server->vol.util,
|
||||
const XP_UCHAR* subString = dutil_getUserString( server->vol.dutil,
|
||||
STRD_UNUSED_TILES_SUB );
|
||||
XP_UCHAR* timeStr;
|
||||
CurGameInfo* gi = server->vol.gi;
|
||||
|
@ -3279,8 +3283,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
XP_U16 penalty = player_timePenalty( gi, thisIndex );
|
||||
if ( penalty > 0 ) {
|
||||
XP_SNPRINTF( timeBuf, sizeof(timeBuf),
|
||||
util_getUserString(
|
||||
server->vol.util,
|
||||
dutil_getUserString( server->vol.dutil,
|
||||
STRD_TIME_PENALTY_SUB ),
|
||||
penalty ); /* positive for formatting */
|
||||
timeStr = timeBuf;
|
||||
|
@ -3296,12 +3299,12 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
|
||||
const XP_UCHAR* name = emptyStringIfNull(gi->players[thisIndex].name);
|
||||
if ( 0 == placeKey ) {
|
||||
const XP_UCHAR* fmt = util_getUserString( server->vol.util,
|
||||
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
|
||||
STRDSD_PLACER );
|
||||
XP_SNPRINTF( buf, sizeof(buf), fmt, place,
|
||||
name, scores.arr[thisIndex] );
|
||||
} else {
|
||||
const XP_UCHAR* fmt = util_getUserString( server->vol.util,
|
||||
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
|
||||
placeKey );
|
||||
XP_SNPRINTF( buf, sizeof(buf), fmt, name,
|
||||
scores.arr[thisIndex] );
|
||||
|
|
|
@ -30,20 +30,6 @@
|
|||
extern "C" {
|
||||
#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 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 );
|
||||
} /* 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 );
|
||||
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
|
||||
#ifdef MEM_DEBUG
|
||||
, const char* file, const char* func, XP_U32 lineNo
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*-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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -26,9 +26,8 @@
|
|||
#include "dawg.h"
|
||||
#include "model.h"
|
||||
#include "board.h"
|
||||
#include "mempool.h"
|
||||
#include "vtabmgr.h"
|
||||
#include "comms.h"
|
||||
#include "dutil.h"
|
||||
|
||||
#include "xwrelay.h"
|
||||
|
||||
|
@ -87,8 +86,6 @@ typedef XP_Bool (*XWTimerProc)( void* closure, XWTimerReason why );
|
|||
*/
|
||||
typedef struct UtilVtable {
|
||||
|
||||
VTableMgr* (*m_util_getVTManager)(XW_UtilCtxt* uc);
|
||||
|
||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||
XWStreamCtxt* (*m_util_makeStreamFromAddr )(XW_UtilCtxt* uc,
|
||||
XP_PlayerAddr channelNo );
|
||||
|
@ -144,21 +141,8 @@ typedef struct UtilVtable {
|
|||
void (*m_util_requestTime)( 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 );
|
||||
|
||||
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,
|
||||
XP_U16 turn, XP_Bool turnLost );
|
||||
|
||||
|
@ -172,11 +156,6 @@ typedef struct UtilVtable {
|
|||
void (*m_util_cellSquareHeld)( XW_UtilCtxt* uc, XWStreamCtxt* words );
|
||||
#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
|
||||
void (*m_util_informMissing)(XW_UtilCtxt* uc, XP_Bool isServer,
|
||||
const CommsAddrRec* addr, XP_U16 nDevs,
|
||||
|
@ -201,9 +180,7 @@ typedef struct UtilVtable {
|
|||
void (*m_util_engineStopping)( XW_UtilCtxt* uc );
|
||||
#endif
|
||||
|
||||
#ifdef COMMS_CHECKSUM
|
||||
XP_UCHAR* (*m_util_md5sum)( XW_UtilCtxt* uc, const XP_U8* ptr, XP_U16 len );
|
||||
#endif
|
||||
XW_DUtilCtxt* (*m_util_getDevUtilCtxt)( XW_UtilCtxt* uc );
|
||||
|
||||
} UtilVtable;
|
||||
|
||||
|
@ -217,9 +194,6 @@ struct XW_UtilCtxt {
|
|||
MPSLOT
|
||||
};
|
||||
|
||||
#define util_getVTManager(uc) \
|
||||
(uc)->vtable->m_util_getVTManager((uc))
|
||||
|
||||
#define util_makeStreamFromAddr(uc,a) \
|
||||
(uc)->vtable->m_util_makeStreamFromAddr((uc),(a))
|
||||
|
||||
|
@ -286,24 +260,9 @@ struct XW_UtilCtxt {
|
|||
#define 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 ) \
|
||||
(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 ) \
|
||||
(uc)->vtable->m_util_notifyIllegalWords((uc),(w),(p),(b))
|
||||
|
||||
|
@ -320,10 +279,6 @@ struct XW_UtilCtxt {
|
|||
#define util_cellSquareHeld(uc, s) \
|
||||
(uc)->vtable->m_util_cellSquareHeld( (uc), (s) )
|
||||
#endif
|
||||
#ifdef XWFEATURE_SMS
|
||||
#define util_phoneNumbersSame(uc,p1,p2) \
|
||||
(uc)->vtable->m_util_phoneNumbersSame( (uc), (p1), (p2) )
|
||||
#endif
|
||||
|
||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||
# define util_informMissing( uc, is, ct, nd, nm ) \
|
||||
|
@ -355,8 +310,7 @@ struct XW_UtilCtxt {
|
|||
# define util_engineStopping( uc )
|
||||
# endif
|
||||
|
||||
#ifdef COMMS_CHECKSUM
|
||||
# define util_md5sum( uc, p, l ) (uc)->vtable->m_util_md5sum((uc), (p), (l))
|
||||
#endif
|
||||
# define util_getDevUtilCtxt(uc) \
|
||||
(uc)->vtable->m_util_getDevUtilCtxt( (uc) )
|
||||
|
||||
#endif
|
||||
|
|
1
xwords4/linux/.gitignore
vendored
1
xwords4/linux/.gitignore
vendored
|
@ -8,3 +8,4 @@ discon_ok2.sh_logs
|
|||
test_backsend.sh_*
|
||||
*.db
|
||||
dawg2dict
|
||||
discon_ok2.py_logs
|
|
@ -225,6 +225,7 @@ OBJ = \
|
|||
$(BUILD_PLAT_DIR)/linuxutl.o \
|
||||
$(BUILD_PLAT_DIR)/gamesdb.o \
|
||||
$(BUILD_PLAT_DIR)/relaycon.o \
|
||||
$(BUILD_PLAT_DIR)/lindutil.o \
|
||||
$(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS)
|
||||
|
||||
LIBS = -lm -lpthread -luuid -lcurl -ljson-c $(GPROFFLAG)
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "linuxudp.h"
|
||||
#include "gamesdb.h"
|
||||
#include "relaycon.h"
|
||||
#include "smsproto.h"
|
||||
|
||||
#ifdef CURSES_SMALL_SCREEN
|
||||
# define MENU_WINDOW_HEIGHT 1
|
||||
|
@ -346,9 +347,8 @@ cursesShowFinalScores( CursesAppGlobals* globals )
|
|||
XWStreamCtxt* stream;
|
||||
XP_UCHAR* text;
|
||||
|
||||
stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
|
||||
globals->cGlobals.params->vtMgr,
|
||||
globals, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(globals->cGlobals.util->mpool)
|
||||
globals->cGlobals.params->vtMgr );
|
||||
server_writeFinalScores( globals->cGlobals.game.server, stream );
|
||||
|
||||
text = strFromStream( stream );
|
||||
|
@ -1363,13 +1363,6 @@ initClientSocket( CursesAppGlobals* globals, char* serverName )
|
|||
} /* initClientSocket */
|
||||
#endif
|
||||
|
||||
static VTableMgr*
|
||||
curses_util_getVTManager(XW_UtilCtxt* uc)
|
||||
{
|
||||
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
|
||||
return globals->cGlobals.params->vtMgr;
|
||||
} /* linux_util_getVTManager */
|
||||
|
||||
static void
|
||||
curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc),
|
||||
XP_U16 XP_UNUSED_DBG(playerNum),
|
||||
|
@ -1413,9 +1406,8 @@ curses_util_remSelected( XW_UtilCtxt* uc )
|
|||
XWStreamCtxt* stream;
|
||||
XP_UCHAR* text;
|
||||
|
||||
stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
|
||||
globals->cGlobals.params->vtMgr,
|
||||
globals, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(globals->cGlobals.util->mpool)
|
||||
globals->cGlobals.params->vtMgr );
|
||||
board_formatRemainingTiles( globals->cGlobals.game.board, 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_getVTManager = curses_util_getVTManager;
|
||||
util->vtable->m_util_informNeedPassword = curses_util_informNeedPassword;
|
||||
util->vtable->m_util_yOffsetChange = curses_util_yOffsetChange;
|
||||
#ifdef XWFEATURE_TURNCHANGENOTIFY
|
||||
|
@ -1740,7 +1731,8 @@ curses_requestMsgs( gpointer data )
|
|||
{
|
||||
CursesAppGlobals* globals = (CursesAppGlobals*)data;
|
||||
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] ) {
|
||||
relaycon_requestMsgs( globals->cGlobals.params, devIDBuf );
|
||||
} else {
|
||||
|
@ -1772,13 +1764,13 @@ cursesDevIDReceived( void* closure, const XP_UCHAR* devID,
|
|||
{
|
||||
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
|
||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||
sqlite3* pDb = cGlobals->pDb;
|
||||
sqlite3* pDb = cGlobals->params->pDb;
|
||||
if ( !!devID ) {
|
||||
XP_LOGF( "%s(devID=%s)", __func__, devID );
|
||||
|
||||
/* If we already have one, make sure it's the same! Else store. */
|
||||
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 );
|
||||
if ( !have ) {
|
||||
db_store( pDb, KEY_RDEVID, devID );
|
||||
|
@ -1998,9 +1990,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
|
||||
XP_Bool idIsNew = XP_TRUE;
|
||||
if ( !!params->dbName ) {
|
||||
sqlite3* pDb = openGamesDB( params->dbName );
|
||||
/* Gross that both need to be set, but they do. */
|
||||
params->pDb = g_globals.cGlobals.pDb = pDb;
|
||||
params->pDb = openGamesDB( params->dbName );
|
||||
|
||||
/* Check if we have a local ID already. If we do and it's
|
||||
changed, we care. */
|
||||
|
@ -2026,18 +2016,18 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
|
||||
#ifdef XWFEATURE_SMS
|
||||
gchar buf[32];
|
||||
const gchar* myPhone = params->connInfo.sms.phone;
|
||||
const gchar* myPhone = params->connInfo.sms.myPhone;
|
||||
if ( !!myPhone ) {
|
||||
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
||||
} else if ( !myPhone && db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||
params->connInfo.sms.phone = myPhone = buf;
|
||||
} else if ( !myPhone && db_fetch_safe( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||
params->connInfo.sms.myPhone = myPhone = buf;
|
||||
}
|
||||
XP_U16 myPort = params->connInfo.sms.port;
|
||||
gchar portbuf[8];
|
||||
if ( 0 < myPort ) {
|
||||
sprintf( portbuf, "%d", myPort );
|
||||
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 );
|
||||
}
|
||||
|
||||
|
@ -2049,6 +2039,11 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
};
|
||||
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
|
||||
|
||||
XWStreamCtxt* stream = NULL;
|
||||
|
@ -2056,9 +2051,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
GSList* games = listGames( params->pDb );
|
||||
if ( !!games ) {
|
||||
XP_ASSERT( 1 == g_slist_length(games) ); /* for now */
|
||||
stream = mem_stream_make( MEMPOOL params->vtMgr,
|
||||
&g_globals.cGlobals, CHANNEL_NONE,
|
||||
NULL );
|
||||
stream = mem_stream_make_raw( MEMPOOL params->vtMgr);
|
||||
sqlite3_int64 selRow = *(sqlite3_int64*)games->data;
|
||||
/* XP_UCHAR buf[32]; */
|
||||
/* 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 ) ) {
|
||||
mpool_setTag( MEMPOOL "file" );
|
||||
stream = streamFromFile( &g_globals.cGlobals, params->fileName,
|
||||
&g_globals );
|
||||
stream = streamFromFile( &g_globals.cGlobals, params->fileName );
|
||||
#ifdef USE_SQLITE
|
||||
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
||||
mpool_setTag( MEMPOOL buf );
|
||||
stream = streamFromDB( &g_globals.cGlobals, &g_globals );
|
||||
stream = streamFromDB( &g_globals.cGlobals );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2141,7 +2133,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
# ifdef XWFEATURE_SMS
|
||||
case 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 );
|
||||
addr.u.sms.port = params->connInfo.sms.port;
|
||||
break;
|
||||
|
@ -2238,7 +2230,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
|||
endwin();
|
||||
|
||||
if ( !!params->dbName ) {
|
||||
closeGamesDB( g_globals.cGlobals.pDb );
|
||||
closeGamesDB( params->pDb );
|
||||
}
|
||||
relaycon_cleanup( params );
|
||||
|
||||
|
|
|
@ -27,8 +27,9 @@
|
|||
#define SNAP_WIDTH 150
|
||||
#define SNAP_HEIGHT 150
|
||||
|
||||
static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
||||
int len );
|
||||
static XP_Bool getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
||||
int* len );
|
||||
|
||||
#ifdef DEBUG
|
||||
static char* sqliteErr2str( int err );
|
||||
#endif
|
||||
|
@ -160,13 +161,16 @@ writeToDB( XWStreamCtxt* stream, void* closure )
|
|||
{
|
||||
CommonGlobals* cGlobals = (CommonGlobals*)closure;
|
||||
sqlite3_int64 selRow = cGlobals->selRow;
|
||||
sqlite3* pDb = cGlobals->pDb;
|
||||
sqlite3* pDb = cGlobals->params->pDb;
|
||||
|
||||
XP_Bool newGame = -1 == selRow;
|
||||
selRow = writeBlobColumnStream( stream, pDb, selRow, "game" );
|
||||
|
||||
if ( newGame ) { /* new row; need to insert blob first */
|
||||
cGlobals->selRow = selRow;
|
||||
XP_LOGF( "%s(): new game at row %lld", __func__, selRow );
|
||||
} else {
|
||||
assert( selRow == cGlobals->selRow );
|
||||
}
|
||||
|
||||
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
|
||||
|
@ -184,11 +188,12 @@ addSnapshot( CommonGlobals* cGlobals )
|
|||
addSurface( 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 );
|
||||
removeSurface( dctx );
|
||||
writeBlobColumnStream( stream, cGlobals->pDb, cGlobals->selRow, "snap" );
|
||||
// XP_ASSERT( cGlobals->selRow == newRow );
|
||||
cGlobals->selRow = writeBlobColumnStream( stream, cGlobals->params->pDb,
|
||||
cGlobals->selRow, "snap" );
|
||||
stream_destroy( stream );
|
||||
}
|
||||
|
||||
|
@ -264,8 +269,8 @@ summarize( CommonGlobals* cGlobals )
|
|||
cGlobals->selRow );
|
||||
XP_LOGF( "query: %s", buf );
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL );
|
||||
assertPrintResult( cGlobals->pDb, result, SQLITE_OK );
|
||||
int result = sqlite3_prepare_v2( cGlobals->params->pDb, buf, -1, &stmt, NULL );
|
||||
assertPrintResult( cGlobals->params->pDb, result, SQLITE_OK );
|
||||
result = sqlite3_step( stmt );
|
||||
if ( SQLITE_DONE != result ) {
|
||||
XP_LOGF( "sqlite3_step=>%s", sqliteErr2str( result ) );
|
||||
|
@ -337,7 +342,8 @@ getRelayIDsToRowsMap( sqlite3* pDb )
|
|||
case SQLITE_ROW: /* have data */
|
||||
{
|
||||
XP_UCHAR relayID[32];
|
||||
getColumnText( ppStmt, 0, relayID, VSIZE(relayID) );
|
||||
int len = VSIZE(relayID);
|
||||
getColumnText( ppStmt, 0, relayID, &len );
|
||||
gpointer key = g_strdup( relayID );
|
||||
sqlite3_int64* value = g_malloc( sizeof( value ) );
|
||||
*value = sqlite3_column_int64( ppStmt, 1 );
|
||||
|
@ -375,7 +381,8 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
|
|||
if ( SQLITE_ROW == result ) {
|
||||
success = XP_TRUE;
|
||||
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->turn = 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->nMissing = 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->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 );
|
||||
|
||||
#ifdef PLATFORM_GTK
|
||||
|
@ -488,6 +497,7 @@ loadInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
|
|||
void
|
||||
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
||||
{
|
||||
XP_ASSERT( !!pDb );
|
||||
char query[256];
|
||||
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
|
||||
sqlite3_stmt* ppStmt;
|
||||
|
@ -502,9 +512,9 @@ deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
|||
void
|
||||
db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
||||
{
|
||||
char buf[256];
|
||||
snprintf( buf, sizeof(buf),
|
||||
"INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
||||
XP_ASSERT( !!pDb );
|
||||
gchar* buf =
|
||||
g_strdup_printf( "INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
||||
key, value );
|
||||
sqlite3_stmt *ppStmt;
|
||||
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 );
|
||||
XP_USE( result );
|
||||
sqlite3_finalize( ppStmt );
|
||||
g_free( buf );
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen )
|
||||
FetchResult
|
||||
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint* buflen )
|
||||
{
|
||||
XP_ASSERT( !!pDb );
|
||||
FetchResult result = NOT_THERE;
|
||||
char query[256];
|
||||
snprintf( query, sizeof(query),
|
||||
"SELECT value from pairs where key = '%s'", key );
|
||||
sqlite3_stmt *ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
XP_Bool found = SQLITE_OK == result;
|
||||
int sqlResult = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
XP_Bool found = SQLITE_OK == sqlResult;
|
||||
if ( found ) {
|
||||
result = sqlite3_step( ppStmt );
|
||||
found = SQLITE_ROW == result;
|
||||
if ( found ) {
|
||||
getColumnText( ppStmt, 0, buf, buflen );
|
||||
if ( getColumnText( ppStmt, 0, buf, buflen ) ) {
|
||||
result = SUCCESS;
|
||||
} else {
|
||||
result = BUFFER_TOO_SMALL;
|
||||
}
|
||||
} else if ( !!buf ) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
}
|
||||
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
|
||||
db_remove( sqlite3* pDb, const gchar* key )
|
||||
{
|
||||
XP_ASSERT( !!pDb );
|
||||
char query[256];
|
||||
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
|
||||
sqlite3_stmt *ppStmt;
|
||||
|
@ -551,15 +579,19 @@ db_remove( sqlite3* pDb, const gchar* key )
|
|||
sqlite3_finalize( ppStmt );
|
||||
}
|
||||
|
||||
static void
|
||||
getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
||||
int XP_UNUSED_DBG(len) )
|
||||
static XP_Bool
|
||||
getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf, int *len )
|
||||
{
|
||||
const unsigned char* txt = sqlite3_column_text( ppStmt, iCol );
|
||||
int needLen = sqlite3_column_bytes( ppStmt, iCol );
|
||||
XP_ASSERT( needLen < len );
|
||||
XP_MEMCPY( buf, txt, needLen );
|
||||
buf[needLen] = '\0';
|
||||
int colLen = sqlite3_column_bytes( ppStmt, iCol );
|
||||
|
||||
XP_Bool success = colLen < *len;
|
||||
*len = colLen + 1; /* sqlite does not store the null byte */
|
||||
if ( success ) {
|
||||
const unsigned char* colTxt = sqlite3_column_text( ppStmt, iCol );
|
||||
XP_MEMCPY( buf, colTxt, colLen );
|
||||
buf[colLen] = '\0';
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -47,7 +47,7 @@ typedef struct _GameInfo {
|
|||
} GameInfo;
|
||||
|
||||
sqlite3* openGamesDB( const char* dbName );
|
||||
void closeGamesDB( sqlite3* dbp );
|
||||
void closeGamesDB( sqlite3* pDb );
|
||||
|
||||
void writeToDB( XWStreamCtxt* stream, void* closure );
|
||||
sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
|
||||
|
@ -55,15 +55,15 @@ sqlite3_int64 writeNewGameToDB( XWStreamCtxt* stream, sqlite3* pDb );
|
|||
void summarize( CommonGlobals* cGlobals );
|
||||
|
||||
/* Return GSList whose data is (ptrs to) rowids */
|
||||
GSList* listGames( sqlite3* dbp );
|
||||
GSList* listGames( sqlite3* pDb );
|
||||
/* free list and data allocated by above */
|
||||
void freeGamesList( GSList* games );
|
||||
|
||||
/* Mapping of relayID -> rowid */
|
||||
GHashTable* getRelayIDsToRowsMap( sqlite3* pDb );
|
||||
|
||||
XP_Bool getGameInfo( sqlite3* dbp, sqlite3_int64 rowid, GameInfo* gib );
|
||||
void getRowsForGameID( sqlite3* dbp, XP_U32 gameID, sqlite3_int64* rowids,
|
||||
XP_Bool getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib );
|
||||
void getRowsForGameID( sqlite3* pDb, XP_U32 gameID, sqlite3_int64* rowids,
|
||||
int* nRowIDs );
|
||||
XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid );
|
||||
void saveInviteAddrs( XWStreamCtxt* stream, sqlite3* pDb,
|
||||
|
@ -78,8 +78,12 @@ void deleteGame( sqlite3* pDb, sqlite3_int64 rowid );
|
|||
#define KEY_SMSPORT "SMSPORT"
|
||||
#define KEY_WIN_LOC "WIN_LOC"
|
||||
|
||||
void db_store( sqlite3* dbp, const gchar* key, const gchar* value );
|
||||
XP_Bool db_fetch( sqlite3* dbp, const gchar* key, gchar* buf, gint buflen );
|
||||
void db_remove( sqlite3* dbp, const gchar* key );
|
||||
void db_store( sqlite3* pDb, const gchar* key, const gchar* value );
|
||||
void db_remove( sqlite3* pDb, 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
|
||||
|
|
|
@ -86,7 +86,7 @@ static void gtkShowFinalScores( const GtkGameGlobals* globals,
|
|||
XP_Bool ignoreTimeout );
|
||||
static void send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||
XP_U32 devID, const XP_UCHAR* relayID,
|
||||
const XP_UCHAR* phone );
|
||||
const CommsAddrRec* addrs );
|
||||
|
||||
|
||||
#define GTK_TRAY_HT_ROWS 3
|
||||
|
@ -589,19 +589,18 @@ createOrLoadObjects( GtkGameGlobals* globals )
|
|||
setTransportProcs( &procs, globals );
|
||||
|
||||
if ( !!params->fileName && file_exists( params->fileName ) ) {
|
||||
stream = streamFromFile( cGlobals, params->fileName, globals );
|
||||
stream = streamFromFile( cGlobals, params->fileName );
|
||||
#ifdef USE_SQLITE
|
||||
} else if ( !!params->dbFileName && file_exists( params->dbFileName ) ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_SNPRINTF( buf, sizeof(buf), "%d", params->dbFileID );
|
||||
mpool_setTag( MEMPOOL buf );
|
||||
stream = streamFromDB( cGlobals, globals );
|
||||
stream = streamFromDB( cGlobals );
|
||||
#endif
|
||||
} else if ( !!cGlobals->pDb && 0 <= cGlobals->selRow ) {
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr,
|
||||
cGlobals, CHANNEL_NONE, NULL );
|
||||
if ( !loadGame( stream, cGlobals->pDb, cGlobals->selRow ) ) {
|
||||
} else if ( !!params->pDb && 0 <= cGlobals->selRow ) {
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
if ( !loadGame( stream, params->pDb, cGlobals->selRow ) ) {
|
||||
stream_destroy( stream );
|
||||
stream = NULL;
|
||||
}
|
||||
|
@ -874,10 +873,9 @@ on_board_window_shown( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|||
CommsCtxt* comms = cGlobals->game.comms;
|
||||
if ( !!comms /*&& COMMS_CONN_NONE == comms_getConTypes( comms )*/ ) {
|
||||
/* If it has pending invite info, send the invitation! */
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
cGlobals, CHANNEL_NONE, NULL );
|
||||
if ( loadInviteAddrs( stream, cGlobals->pDb, cGlobals->selRow ) ) {
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr );
|
||||
if ( loadInviteAddrs( stream, cGlobals->params->pDb, cGlobals->selRow ) ) {
|
||||
CommsAddrRec addr = {0};
|
||||
addrFromStream( &addr, stream );
|
||||
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 );
|
||||
|
||||
if ( confirmed ) {
|
||||
send_invites( cGlobals, nPlayers, devID, NULL, NULL );
|
||||
send_invites( cGlobals, nPlayers, devID, NULL, &inviteAddr );
|
||||
}
|
||||
} /* handle_invite_button */
|
||||
|
||||
static void
|
||||
send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
||||
XP_U32 devID, const XP_UCHAR* relayID,
|
||||
const XP_UCHAR* phone )
|
||||
const CommsAddrRec* addrs )
|
||||
{
|
||||
CommsAddrRec addr = {0};
|
||||
CommsCtxt* comms = cGlobals->game.comms;
|
||||
XP_ASSERT( comms );
|
||||
comms_getAddr( comms, &addr );
|
||||
|
||||
gint forceChannel = 0; /* PENDING */
|
||||
gint forceChannel = 1; /* 1 is what Android does. Limits to two-device games */
|
||||
|
||||
NetLaunchInfo nli = {0};
|
||||
nli_init( &nli, cGlobals->gi, &addr, nPlayers, forceChannel );
|
||||
|
@ -1647,9 +1645,8 @@ send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
|||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr );
|
||||
nli_saveToStream( &nli, stream );
|
||||
NetLaunchInfo tmp;
|
||||
nli_makeFromStream( &tmp, stream );
|
||||
|
@ -1658,11 +1655,13 @@ send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers,
|
|||
}
|
||||
#endif
|
||||
|
||||
if ( !!phone ) {
|
||||
XP_ASSERT( 0 ); /* not implemented */
|
||||
/* linux_sms_invite( cGlobals->params, gi, &addr, gameName, */
|
||||
/* nPlayers, forceChannel, */
|
||||
/* inviteAddr.u.sms.phone, inviteAddr.u.sms.port ); */
|
||||
if ( !!addrs->u.sms.phone ) {
|
||||
gchar gameName[64];
|
||||
snprintf( gameName, VSIZE(gameName), "Game %d", cGlobals->gi->gameID );
|
||||
|
||||
linux_sms_invite( cGlobals->params, cGlobals->gi, &addr, gameName,
|
||||
nPlayers, forceChannel,
|
||||
addrs->u.sms.phone, addrs->u.sms.port );
|
||||
}
|
||||
if ( 0 != devID || !!relayID ) {
|
||||
XP_ASSERT( 0 != devID || (!!relayID && !!relayID[0]) );
|
||||
|
@ -1722,14 +1721,6 @@ gtkUserError( GtkGameGlobals* globals, const char* format, ... )
|
|||
va_end(ap);
|
||||
} /* gtkUserError */
|
||||
|
||||
static VTableMgr*
|
||||
gtk_util_getVTManager(XW_UtilCtxt* uc)
|
||||
{
|
||||
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
||||
return globals->cGlobals.params->vtMgr;
|
||||
} /* linux_util_getVTManager */
|
||||
|
||||
|
||||
static gint
|
||||
ask_blank( gpointer data )
|
||||
{
|
||||
|
@ -1934,9 +1925,8 @@ gtkShowFinalScores( const GtkGameGlobals* globals, XP_Bool ignoreTimeout )
|
|||
XP_UCHAR* text;
|
||||
const CommonGlobals* cGlobals = &globals->cGlobals;
|
||||
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr );
|
||||
server_writeFinalScores( cGlobals->game.server, stream );
|
||||
|
||||
text = strFromStream( stream );
|
||||
|
@ -2272,9 +2262,8 @@ gtk_util_remSelected( XW_UtilCtxt* uc )
|
|||
XWStreamCtxt* stream;
|
||||
XP_UCHAR* text;
|
||||
|
||||
stream = mem_stream_make( MEMPOOL
|
||||
globals->cGlobals.params->vtMgr,
|
||||
globals, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MEMPOOL
|
||||
globals->cGlobals.params->vtMgr );
|
||||
board_formatRemainingTiles( globals->cGlobals.game.board, stream );
|
||||
text = strFromStream( 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_notifyMove = gtk_util_notifyMove;
|
||||
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_informNeedPickTiles = gtk_util_informNeedPickTiles;
|
||||
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
|
||||
|
@ -2935,11 +2923,9 @@ loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
|
|||
|
||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||
cGlobals->selRow = rowid;
|
||||
cGlobals->pDb = pDb;
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr, cGlobals,
|
||||
CHANNEL_NONE, NULL );
|
||||
XP_Bool loaded = loadGame( stream, cGlobals->pDb, rowid );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
XP_Bool loaded = loadGame( stream, pDb, rowid );
|
||||
if ( loaded ) {
|
||||
if ( NULL == cGlobals->dict ) {
|
||||
cGlobals->dict = makeDictForStream( cGlobals, stream );
|
||||
|
|
|
@ -305,7 +305,7 @@ makeSMSPage( GtkConnsState* state, PageData* data )
|
|||
GtkWidget* vbox = boxWithUseCheck( state, data );
|
||||
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
||||
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 );
|
||||
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
||||
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 );
|
||||
XP_Bool hasSMS = addr_hasType( state->addr, data->pageType );
|
||||
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 );
|
||||
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
|
||||
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
#ifdef PLATFORM_GTK
|
||||
|
||||
#include "strutils.h"
|
||||
#include "smsproto.h"
|
||||
#include "main.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gamesdb.h"
|
||||
#include "gtkboard.h"
|
||||
#include "linuxmain.h"
|
||||
#include "linuxutl.h"
|
||||
#include "relaycon.h"
|
||||
#include "linuxsms.h"
|
||||
#include "gtkask.h"
|
||||
|
@ -275,7 +277,6 @@ handle_newgame_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
|||
freeGlobals( globals );
|
||||
} else {
|
||||
GtkWidget* gameWindow = globals->window;
|
||||
globals->cGlobals.pDb = apg->params->pDb;
|
||||
globals->cGlobals.selRow = -1;
|
||||
recordOpened( apg, globals );
|
||||
gtk_widget_show( gameWindow );
|
||||
|
@ -293,7 +294,6 @@ open_row( GtkAppGlobals* apg, sqlite3_int64 row, XP_Bool isNew )
|
|||
apg->params->needsNewGame = XP_FALSE;
|
||||
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
||||
initGlobals( globals, apg->params, NULL );
|
||||
globals->cGlobals.pDb = apg->params->pDb;
|
||||
globals->cGlobals.selRow = row;
|
||||
recordOpened( apg, globals );
|
||||
gtk_widget_show( globals->window );
|
||||
|
@ -315,10 +315,10 @@ handle_open_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
|||
void
|
||||
make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
||||
{
|
||||
// LaunchParams* params = apg->params;
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
LaunchParams* params = apg->params;
|
||||
XP_ASSERT( params == cGlobals->params );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
|
||||
/* Create new game. But has no addressing info, so need to set that
|
||||
aside for later. */
|
||||
|
@ -333,16 +333,15 @@ make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
|||
game_saveNewGame( MPPARM(cGlobals->util->mpool) &gi,
|
||||
cGlobals->util, &cGlobals->cp, stream );
|
||||
|
||||
sqlite3_int64 rowID = writeNewGameToDB( stream, cGlobals->pDb );
|
||||
sqlite3_int64 rowID = writeNewGameToDB( stream, params->pDb );
|
||||
stream_destroy( stream );
|
||||
gi_disposePlayerInfo( MPPARM(cGlobals->util->mpool) &gi );
|
||||
|
||||
/* 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. */
|
||||
if ( !!comms ) {
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
CommsAddrRec addr;
|
||||
comms_getAddr( comms, &addr );
|
||||
addrToStream( stream, &addr );
|
||||
|
@ -364,7 +363,7 @@ make_rematch( GtkAppGlobals* apg, const CommonGlobals* cGlobals )
|
|||
}
|
||||
addrToStream( stream, &addrs[ii] );
|
||||
}
|
||||
saveInviteAddrs( stream, cGlobals->pDb, rowID );
|
||||
saveInviteAddrs( stream, params->pDb, rowID );
|
||||
stream_destroy( stream );
|
||||
}
|
||||
|
||||
|
@ -406,7 +405,7 @@ handle_delete_button( GtkWidget* XP_UNUSED(widget), void* closure )
|
|||
deleteGame( params->pDb, rowid );
|
||||
|
||||
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] ) {
|
||||
relaycon_deleted( params, devIDBuf, clientToken );
|
||||
} else {
|
||||
|
@ -482,7 +481,7 @@ setWindowTitle( GtkAppGlobals* apg )
|
|||
#ifdef XWFEATURE_SMS
|
||||
int len = strlen( title );
|
||||
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
|
||||
#ifdef XWFEATURE_RELAY
|
||||
XP_U32 relayID = linux_getDevIDRelay( params );
|
||||
|
@ -501,7 +500,7 @@ trySetWinConfig( GtkAppGlobals* apg )
|
|||
int height = 400;
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
|
@ -684,7 +683,6 @@ relayInviteReceived( void* closure, NetLaunchInfo* invite )
|
|||
// globals->cGlobals.addr = *returnAddr;
|
||||
|
||||
GtkWidget* gameWindow = globals->window;
|
||||
globals->cGlobals.pDb = apg->params->pDb;
|
||||
globals->cGlobals.selRow = -1;
|
||||
recordOpened( apg, globals );
|
||||
gtk_widget_show( gameWindow );
|
||||
|
@ -730,7 +728,7 @@ requestMsgs( gpointer data )
|
|||
{
|
||||
GtkAppGlobals* apg = (GtkAppGlobals*)data;
|
||||
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] ) {
|
||||
relaycon_requestMsgs( apg->params, devIDBuf );
|
||||
} else {
|
||||
|
@ -755,8 +753,9 @@ smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName),
|
|||
{
|
||||
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
|
||||
LaunchParams* params = apg->params;
|
||||
XP_LOGF( "%s(gameName=%s, gameID=%d, dictName=%s, nPlayers=%d, nHere=%d)",
|
||||
__func__, gameName, gameID, dictName, nPlayers, nHere );
|
||||
XP_LOGF( "%s(gameName=%s, gameID=%d, dictName=%s, nPlayers=%d, "
|
||||
"nHere=%d, forceChannel=%d)", __func__, gameName, gameID, dictName,
|
||||
nPlayers, nHere, forceChannel );
|
||||
|
||||
CurGameInfo gi = {0};
|
||||
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.dictLang = dictLang;
|
||||
gi.forceChannel = forceChannel;
|
||||
gi.serverRole = SERVER_ISCLIENT; /* recipient of invitation is client */
|
||||
replaceStringIfDifferent( params->mpool, &gi.dictName, dictName );
|
||||
|
||||
GtkGameGlobals* globals = malloc( sizeof(*globals) );
|
||||
|
@ -773,7 +773,6 @@ smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName),
|
|||
globals->cGlobals.addr = *returnAddr;
|
||||
|
||||
GtkWidget* gameWindow = globals->window;
|
||||
globals->cGlobals.pDb = apg->params->pDb;
|
||||
globals->cGlobals.selRow = -1;
|
||||
recordOpened( apg, globals );
|
||||
gtk_widget_show( gameWindow );
|
||||
|
@ -896,18 +895,18 @@ gtkmain( LaunchParams* params )
|
|||
|
||||
#ifdef XWFEATURE_SMS
|
||||
gchar buf[32];
|
||||
const gchar* myPhone = params->connInfo.sms.phone;
|
||||
const gchar* myPhone = params->connInfo.sms.myPhone;
|
||||
if ( !!myPhone ) {
|
||||
db_store( params->pDb, KEY_SMSPHONE, myPhone );
|
||||
} else if ( !myPhone && db_fetch( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||
params->connInfo.sms.phone = myPhone = buf;
|
||||
} else if ( !myPhone && db_fetch_safe( params->pDb, KEY_SMSPHONE, buf, VSIZE(buf) ) ) {
|
||||
params->connInfo.sms.myPhone = myPhone = buf;
|
||||
}
|
||||
XP_U16 myPort = params->connInfo.sms.port;
|
||||
gchar portbuf[8];
|
||||
if ( 0 < myPort ) {
|
||||
sprintf( portbuf, "%d", myPort );
|
||||
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 );
|
||||
}
|
||||
if ( !!myPhone && 0 < myPort ) {
|
||||
|
@ -921,7 +920,13 @@ gtkmain( LaunchParams* params )
|
|||
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
|
||||
makeGamesWindow( &apg );
|
||||
} 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 "gamesdb.h"
|
||||
#include "linuxdict.h"
|
||||
#include "lindutil.h"
|
||||
#include "relaycon.h"
|
||||
#include "smsproto.h"
|
||||
#ifdef PLATFORM_NCURSES
|
||||
# include "cursesmain.h"
|
||||
#endif
|
||||
|
@ -95,7 +97,7 @@ file_exists( const char* fileName )
|
|||
} /* file_exists */
|
||||
|
||||
XWStreamCtxt*
|
||||
streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
||||
streamFromFile( CommonGlobals* cGlobals, char* name )
|
||||
{
|
||||
XP_U8* buf;
|
||||
struct stat statBuf;
|
||||
|
@ -110,9 +112,8 @@ streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
|||
}
|
||||
fclose( f );
|
||||
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
closure, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr );
|
||||
stream_putBytes( stream, buf, statBuf.st_size );
|
||||
free( buf );
|
||||
|
||||
|
@ -121,7 +122,7 @@ streamFromFile( CommonGlobals* cGlobals, char* name, void* closure )
|
|||
|
||||
#ifdef USE_SQLITE
|
||||
XWStreamCtxt*
|
||||
streamFromDB( CommonGlobals* cGlobals, void* closure )
|
||||
streamFromDB( CommonGlobals* cGlobals )
|
||||
{
|
||||
LOG_FUNC();
|
||||
XWStreamCtxt* stream = NULL;
|
||||
|
@ -139,9 +140,8 @@ streamFromDB( CommonGlobals* cGlobals, void* closure )
|
|||
XP_U8 buf[size];
|
||||
res = sqlite3_blob_read( ppBlob, buf, size, 0 );
|
||||
if ( SQLITE_OK == res ) {
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr,
|
||||
closure, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
stream_putBytes( stream, buf, size );
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ requestMsgsIdle( gpointer data )
|
|||
{
|
||||
CommonGlobals* cGlobals = (CommonGlobals*)data;
|
||||
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] ) {
|
||||
relaycon_requestMsgs( cGlobals->params, devIDBuf );
|
||||
} else {
|
||||
|
@ -352,8 +352,9 @@ void
|
|||
saveGame( CommonGlobals* cGlobals )
|
||||
{
|
||||
LOG_FUNC();
|
||||
sqlite3* pDb = cGlobals->params->pDb;
|
||||
if ( !!cGlobals->game.model &&
|
||||
(!!cGlobals->params->fileName || !!cGlobals->pDb) ) {
|
||||
(!!cGlobals->params->fileName || !!pDb) ) {
|
||||
XP_Bool doSave = XP_TRUE;
|
||||
XP_Bool newGame = !file_exists( cGlobals->params->fileName )
|
||||
|| -1 == cGlobals->selRow;
|
||||
|
@ -364,13 +365,12 @@ saveGame( CommonGlobals* cGlobals )
|
|||
}
|
||||
|
||||
if ( doSave ) {
|
||||
if ( !!cGlobals->pDb ) {
|
||||
if ( !!pDb ) {
|
||||
summarize( cGlobals );
|
||||
}
|
||||
|
||||
XWStreamCtxt* outStream;
|
||||
MemStreamCloseCallback onClose = !!cGlobals->pDb?
|
||||
writeToDB : writeToFile;
|
||||
MemStreamCloseCallback onClose = !!pDb? writeToDB : writeToFile;
|
||||
outStream =
|
||||
mem_stream_make_sized( MPPARM(cGlobals->util->mpool)
|
||||
cGlobals->params->vtMgr,
|
||||
|
@ -398,8 +398,7 @@ handle_messages_from( CommonGlobals* cGlobals, const TransportProcs* procs,
|
|||
{
|
||||
LOG_FUNC();
|
||||
LaunchParams* params = cGlobals->params;
|
||||
XWStreamCtxt* stream =
|
||||
streamFromFile( cGlobals, params->fileName, cGlobals );
|
||||
XWStreamCtxt* stream = streamFromFile( cGlobals, params->fileName );
|
||||
|
||||
#ifdef DEBUG
|
||||
XP_Bool opened =
|
||||
|
@ -431,9 +430,8 @@ handle_messages_from( CommonGlobals* cGlobals, const TransportProcs* procs,
|
|||
XP_LOGF( "%s: 2: unexpected nRead: %zd", __func__, nRead );
|
||||
break;
|
||||
}
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr, cGlobals, CHANNEL_NONE,
|
||||
NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
stream_putBytes( stream, buf, len );
|
||||
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
||||
stream_destroy( stream );
|
||||
|
@ -447,8 +445,7 @@ read_pipe_then_close( CommonGlobals* cGlobals, const TransportProcs* procs )
|
|||
{
|
||||
LOG_FUNC();
|
||||
LaunchParams* params = cGlobals->params;
|
||||
XWStreamCtxt* stream =
|
||||
streamFromFile( cGlobals, params->fileName, cGlobals );
|
||||
XWStreamCtxt* stream = streamFromFile( cGlobals, params->fileName );
|
||||
|
||||
#ifdef DEBUG
|
||||
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 );
|
||||
break;
|
||||
}
|
||||
stream = mem_stream_make( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr, cGlobals, CHANNEL_NONE,
|
||||
NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(cGlobals->util->mpool)
|
||||
params->vtMgr );
|
||||
stream_putBytes( stream, buf, len );
|
||||
(void)processMessage( cGlobals, stream, NULL, XP_TRUE );
|
||||
stream_destroy( stream );
|
||||
|
@ -643,6 +639,7 @@ typedef enum {
|
|||
,CMD_DROPSENDRELAY
|
||||
,CMD_DROPRCVRELAY
|
||||
,CMD_DROPSENDSMS
|
||||
,CMD_SMSFAILPCT
|
||||
,CMD_DROPRCVSMS
|
||||
,CMD_FORCECHANNEL
|
||||
|
||||
|
@ -656,6 +653,7 @@ typedef enum {
|
|||
#endif
|
||||
#ifdef XWFEATURE_SMS
|
||||
,CMD_SMSNUMBER /* SMS phone number */
|
||||
,CMD_SERVER_SMSNUMBER
|
||||
,CMD_SMSPORT
|
||||
#endif
|
||||
#ifdef XWFEATURE_RELAY
|
||||
|
@ -684,6 +682,7 @@ typedef enum {
|
|||
,CMD_NHIDDENROWS
|
||||
#endif
|
||||
,CMD_ASKTIME
|
||||
,CMD_SMSTEST
|
||||
,N_CMDS
|
||||
} XwLinuxCmd;
|
||||
|
||||
|
@ -764,6 +763,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
|||
,{ 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_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_FORCECHANNEL, true, "force-channel", "force (clients) to use this hostid/channel" }
|
||||
|
||||
|
@ -779,6 +779,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
|||
#endif
|
||||
#ifdef XWFEATURE_SMS
|
||||
,{ 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" }
|
||||
#endif
|
||||
#ifdef XWFEATURE_RELAY
|
||||
|
@ -811,6 +812,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
|||
,{ CMD_ASKTIME, true, "ask-timeout",
|
||||
"Wait this many ms before cancelling dialog (default 500 ms; 0 means forever)" }
|
||||
#endif
|
||||
,{ CMD_SMSTEST, false, "run-sms-test", "Run smsproto_runTests() on startup"}
|
||||
};
|
||||
|
||||
static struct option*
|
||||
|
@ -910,7 +912,7 @@ linux_getDevIDRelay( LaunchParams* params )
|
|||
{
|
||||
XP_U32 result = 0;
|
||||
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 );
|
||||
XP_LOGF( "%s(): %s => %x", __func__, buf, result );
|
||||
}
|
||||
|
@ -918,6 +920,12 @@ linux_getDevIDRelay( LaunchParams* params )
|
|||
return result;
|
||||
}
|
||||
|
||||
XP_U32
|
||||
linux_getCurSeconds()
|
||||
{
|
||||
return (XP_U32)time(NULL);//tv.tv_sec;
|
||||
}
|
||||
|
||||
const XP_UCHAR*
|
||||
linux_getDevID( LaunchParams* params, DevIDType* typ )
|
||||
{
|
||||
|
@ -928,11 +936,11 @@ linux_getDevID( LaunchParams* params, DevIDType* typ )
|
|||
if ( !!params->lDevID ) {
|
||||
result = params->lDevID;
|
||||
*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) ) ) {
|
||||
result = params->devIDStore;
|
||||
*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) ) ) {
|
||||
result = params->devIDStore;
|
||||
*typ = '\0' == result[0] ? ID_TYPE_ANON : ID_TYPE_LINUX;
|
||||
|
@ -947,7 +955,7 @@ void
|
|||
linux_doInitialReg( LaunchParams* params, XP_Bool idIsNew )
|
||||
{
|
||||
gchar rDevIDBuf[64];
|
||||
if ( !db_fetch( params->pDb, KEY_RDEVID, rDevIDBuf,
|
||||
if ( !db_fetch_safe( params->pDb, KEY_RDEVID, rDevIDBuf,
|
||||
sizeof(rDevIDBuf) ) ) {
|
||||
rDevIDBuf[0] = '\0';
|
||||
}
|
||||
|
@ -964,7 +972,7 @@ linux_setupDevidParams( LaunchParams* params )
|
|||
{
|
||||
XP_Bool idIsNew = XP_TRUE;
|
||||
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 )) ) {
|
||||
idIsNew = XP_FALSE;
|
||||
} 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 );
|
||||
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,
|
||||
addrRec->u.sms.phone, addrRec->u.sms.port,
|
||||
gameID );
|
||||
phone, addrRec->u.sms.port, gameID );
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
@ -1434,9 +1448,8 @@ stream_from_msgbuf( CommonGlobals* globals, const unsigned char* bufPtr,
|
|||
XP_U16 nBytes )
|
||||
{
|
||||
XWStreamCtxt* result;
|
||||
result = mem_stream_make( MPPARM(globals->util->mpool)
|
||||
globals->params->vtMgr,
|
||||
globals, CHANNEL_NONE, NULL );
|
||||
result = mem_stream_make_raw( MPPARM(globals->util->mpool)
|
||||
globals->params->vtMgr );
|
||||
stream_putBytes( result, bufPtr, nBytes );
|
||||
|
||||
return result;
|
||||
|
@ -1950,7 +1963,7 @@ initFromParams( CommonGlobals* cGlobals, LaunchParams* params )
|
|||
#ifdef XWFEATURE_SMS
|
||||
case 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 );
|
||||
addr->u.sms.port = params->connInfo.sms.port;
|
||||
break;
|
||||
|
@ -2001,12 +2014,15 @@ initParams( LaunchParams* params )
|
|||
/* params->util->vtable->m_util_addrChange = linux_util_addrChange; */
|
||||
/* params->util->vtable->m_util_setIsServer = linux_util_setIsServer; */
|
||||
#endif
|
||||
|
||||
params->dutil = dutils_init( MPPARM(params->mpool) params->vtMgr, params );
|
||||
}
|
||||
|
||||
static void
|
||||
freeParams( LaunchParams* params )
|
||||
{
|
||||
vtmgr_destroy( MPPARM(params->mpool) params->vtMgr );
|
||||
dutils_free( ¶ms->dutil );
|
||||
dmgr_destroy( params->dictMgr );
|
||||
|
||||
gi_disposePlayerInfo( MPPARM(params->mpool) ¶ms->pgi );
|
||||
|
@ -2272,7 +2288,11 @@ main( int argc, char** argv )
|
|||
break;
|
||||
#ifdef XWFEATURE_SMS
|
||||
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 );
|
||||
break;
|
||||
case CMD_SMSPORT:
|
||||
|
@ -2427,6 +2447,10 @@ main( int argc, char** argv )
|
|||
case CMD_DROPSENDSMS:
|
||||
mainParams.commsDisableds[COMMS_CONN_SMS][1] = XP_TRUE;
|
||||
break;
|
||||
case CMD_SMSFAILPCT:
|
||||
mainParams.smsSendFailPct = atoi(optarg);
|
||||
XP_ASSERT( mainParams.smsSendFailPct >= 0 && mainParams.smsSendFailPct <= 100 );
|
||||
break;
|
||||
case CMD_DROPRCVSMS:
|
||||
mainParams.commsDisableds[COMMS_CONN_SMS][0] = XP_TRUE;
|
||||
break;
|
||||
|
@ -2488,6 +2512,10 @@ main( int argc, char** argv )
|
|||
mainParams.askTimeout = atoi(optarg);
|
||||
break;
|
||||
#endif
|
||||
case CMD_SMSTEST:
|
||||
mainParams.runSMSTest = XP_TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
done = true;
|
||||
break;
|
||||
|
|
|
@ -64,9 +64,8 @@ void sendOnClose( XWStreamCtxt* stream, void* closure );
|
|||
|
||||
void catFinalScores( const CommonGlobals* cGlobals, XP_S16 quitter );
|
||||
XP_Bool file_exists( const char* fileName );
|
||||
XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name,
|
||||
void* closure );
|
||||
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals, void* closure );
|
||||
XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name );
|
||||
XWStreamCtxt* streamFromDB( CommonGlobals* cGlobals );
|
||||
void writeToFile( XWStreamCtxt* stream, void* closure );
|
||||
XP_Bool getDictPath( const LaunchParams *params, const char* name,
|
||||
char* result, int resultLen );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- 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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -20,15 +20,18 @@
|
|||
|
||||
#ifdef XWFEATURE_SMS
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "linuxsms.h"
|
||||
#include "linuxutl.h"
|
||||
#include "strutils.h"
|
||||
#include "smsproto.h"
|
||||
#include "linuxmain.h"
|
||||
|
||||
#define SMS_DIR "/tmp/xw_sms"
|
||||
#define LOCK_FILE ".lock"
|
||||
|
@ -54,26 +57,34 @@
|
|||
|
||||
#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 {
|
||||
int fd, wd;
|
||||
XP_UCHAR myQueue[256];
|
||||
XP_U16 myPort;
|
||||
FILE* lock;
|
||||
XP_U16 count;
|
||||
|
||||
const gchar* myPhone;
|
||||
const SMSProcs* procs;
|
||||
void* procClosure;
|
||||
SMSProto* protoState;
|
||||
} 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;
|
||||
#define SMS_PROTO_VERSION 0
|
||||
|
||||
|
@ -103,13 +114,24 @@ unlock_queue( LinSMSData* storage )
|
|||
}
|
||||
|
||||
static XP_S16
|
||||
send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
||||
write_fake_sms( LaunchParams* params, XWStreamCtxt* stream,
|
||||
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 );
|
||||
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 );
|
||||
char path[256];
|
||||
|
||||
|
@ -119,17 +141,19 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
|||
gchar* str64 = g_base64_encode( buf, buflen );
|
||||
#endif
|
||||
|
||||
XP_U16 count = ++storage->count;
|
||||
makeQueuePath( phone, port, path, sizeof(path) );
|
||||
XP_LOGF( "%s: writing msg %d to %s", __func__, count, path );
|
||||
formatQueuePath( phone, port, path, sizeof(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 */
|
||||
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_U16 smslen = sizeof(sms);
|
||||
binToSms( sms, &smslen, buf, buflen );
|
||||
XP_ASSERT( smslen == strlen(sms) );
|
||||
XP_LOGF( "%s: writing msg to %s", __func__, path );
|
||||
|
||||
#ifdef DEBUG
|
||||
XP_ASSERT( !strcmp( str64, sms ) );
|
||||
|
@ -139,7 +163,8 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
|||
XP_U16 lenout = sizeof( testout );
|
||||
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
|
||||
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
|
||||
|
||||
FILE* fp = fopen( path, "w" );
|
||||
|
@ -153,8 +178,10 @@ send_sms( LinSMSData* storage, XWStreamCtxt* stream,
|
|||
|
||||
nSent = buflen;
|
||||
|
||||
LOG_RETURNF( "%d", nSent );
|
||||
}
|
||||
return nSent;
|
||||
} /* linux_sms_send */
|
||||
} /* write_fake_sms */
|
||||
|
||||
static XP_S16
|
||||
decodeAndDelete( LinSMSData* storage, const gchar* name,
|
||||
|
@ -185,6 +212,7 @@ decodeAndDelete( LinSMSData* storage, const gchar* name,
|
|||
*strstr(eol, "\n" ) = '\0';
|
||||
|
||||
XP_U16 inlen = strlen(eol); /* skip \n */
|
||||
XP_LOGF( "%s(): decoding message from file %s", __func__, name );
|
||||
XP_U8 out[inlen];
|
||||
XP_U16 outlen = sizeof(out);
|
||||
XP_Bool valid = smsToBin( out, &outlen, eol, inlen );
|
||||
|
@ -194,7 +222,8 @@ decodeAndDelete( LinSMSData* storage, const gchar* name,
|
|||
nRead = outlen;
|
||||
addr_setType( addr, COMMS_CONN_SMS );
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
|
@ -234,13 +263,24 @@ static void
|
|||
dispatch_data( LinSMSData* storage, XP_U16 XP_UNUSED(proto),
|
||||
XWStreamCtxt* stream, const CommsAddrRec* addr )
|
||||
{
|
||||
LOG_FUNC();
|
||||
|
||||
XP_U32 gameID = stream_getU32( stream );
|
||||
XP_U16 len = stream_getSize( stream );
|
||||
XP_U8 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 );
|
||||
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
|
||||
|
@ -248,9 +288,8 @@ parseAndDispatch( LaunchParams* params, uint8_t* buf, int len,
|
|||
CommsAddrRec* addr )
|
||||
{
|
||||
LinSMSData* storage = getStorage( params );
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool)
|
||||
params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||
params->vtMgr );
|
||||
stream_setVersion( stream, CUR_STREAM_VERS );
|
||||
stream_putBytes( stream, buf, len );
|
||||
|
||||
|
@ -274,66 +313,6 @@ parseAndDispatch( LaunchParams* params, uint8_t* buf, int len,
|
|||
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
|
||||
linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort,
|
||||
const SMSProcs* procs, void* procClosure )
|
||||
|
@ -346,18 +325,19 @@ linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort,
|
|||
storage->myPort = myPort;
|
||||
storage->procs = procs;
|
||||
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 );
|
||||
storage->myPort = params->connInfo.sms.port;
|
||||
|
||||
(void)g_mkdir_with_parents( storage->myQueue, 0777 );
|
||||
|
||||
int fd = inotify_init();
|
||||
storage->fd = fd;
|
||||
storage->wd = inotify_add_watch( fd, storage->myQueue, IN_MODIFY );
|
||||
|
||||
(*procs->socketAdded)( params, fd, sms_receive );
|
||||
/* Look for preexisting or new files every half second. Easier than
|
||||
inotify, especially when you add the need to handle files written while
|
||||
not running. */
|
||||
(void)g_idle_add( check_for_files_once, params );
|
||||
(void)g_timeout_add( 500, check_for_files, params );
|
||||
} /* linux_sms_init */
|
||||
|
||||
void
|
||||
|
@ -368,8 +348,7 @@ linux_sms_invite( LaunchParams* params, const CurGameInfo* gi,
|
|||
{
|
||||
LOG_FUNC();
|
||||
XWStreamCtxt* stream;
|
||||
stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
stream = mem_stream_make_raw( MPPARM(params->mpool) params->vtMgr );
|
||||
writeHeader( stream, INVITE );
|
||||
stream_putU32( stream, gi->gameID );
|
||||
stringToStream( stream, gameName );
|
||||
|
@ -381,8 +360,7 @@ linux_sms_invite( LaunchParams* params, const CurGameInfo* gi,
|
|||
|
||||
addrToStream( stream, addr );
|
||||
|
||||
LinSMSData* storage = getStorage( params );
|
||||
send_sms( storage, stream, toPhone, toPort );
|
||||
write_fake_sms( params, stream, toPhone, toPort );
|
||||
|
||||
stream_destroy( stream );
|
||||
}
|
||||
|
@ -391,26 +369,161 @@ XP_S16
|
|||
linux_sms_send( LaunchParams* params, const XP_U8* buf,
|
||||
XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port,
|
||||
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 );
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool) params->vtMgr,
|
||||
NULL, CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||
params->vtMgr );
|
||||
writeHeader( stream, DATA );
|
||||
stream_putU32( stream, gameID );
|
||||
stream_putBytes( stream, buf, buflen );
|
||||
|
||||
LinSMSData* storage = getStorage( params );
|
||||
if ( 0 >= send_sms( storage, stream, phone, port ) ) {
|
||||
buflen = -1;
|
||||
}
|
||||
(void)write_fake_sms( params, stream, phone, port );
|
||||
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
|
||||
linux_sms_cleanup( LaunchParams* params )
|
||||
{
|
||||
LinSMSData* storage = getStorage( params );
|
||||
smsproto_free( storage->protoState );
|
||||
XP_FREEP( params->mpool, ¶ms->smsStorage );
|
||||
}
|
||||
|
||||
|
|
|
@ -266,151 +266,13 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, XP_U16 nCols,
|
|||
return result;
|
||||
} /* linux_util_getSquareBonus */
|
||||
|
||||
static XP_U32
|
||||
linux_util_getCurSeconds( XW_UtilCtxt* XP_UNUSED(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 )
|
||||
static XW_DUtilCtxt*
|
||||
linux_util_getDevUtilCtxt( XW_UtilCtxt* uc )
|
||||
{
|
||||
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
|
||||
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_getSquareBonus = linux_util_getSquareBonus;
|
||||
util->vtable->m_util_getCurSeconds = linux_util_getCurSeconds;
|
||||
util->vtable->m_util_getUserString = linux_util_getUserString;
|
||||
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
|
||||
|
||||
util->vtable->m_util_getDevUtilCtxt = linux_util_getDevUtilCtxt;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -666,16 +517,6 @@ storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 msglen,
|
|||
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
|
||||
writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
|
||||
{
|
||||
|
@ -688,7 +529,8 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
|
|||
guint nMsgs = g_slist_length( list );
|
||||
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_catString( stream, relayID );
|
||||
stream_putU8( stream, '\n' );
|
||||
|
|
|
@ -48,7 +48,6 @@ void initNoConnStorage( CommonGlobals* cGlobals );
|
|||
XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len,
|
||||
const XP_UCHAR* relayID );
|
||||
void writeNoConnMsgs( CommonGlobals* cGlobals, int fd );
|
||||
XWStreamCtxt* make_simple_stream( CommonGlobals* cGlobals );
|
||||
|
||||
#ifdef STREAM_VERS_BIGBOARD
|
||||
void setSquareBonuses( const CommonGlobals* cGlobals );
|
||||
|
@ -56,4 +55,6 @@ void setSquareBonuses( const CommonGlobals* cGlobals );
|
|||
# define setSquareBonuses( cg )
|
||||
#endif
|
||||
|
||||
XP_U32 linux_getCurSeconds();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "game.h"
|
||||
#include "vtabmgr.h"
|
||||
#include "dictmgr.h"
|
||||
#include "dutil.h"
|
||||
|
||||
typedef struct ServerInfo {
|
||||
XP_U16 nRemotePlayers;
|
||||
|
@ -58,6 +59,7 @@ typedef struct LaunchParams {
|
|||
char* dbName;
|
||||
sqlite3* pDb; /* null unless opened */
|
||||
XP_U16 saveFailPct;
|
||||
XP_U16 smsSendFailPct;
|
||||
const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS];
|
||||
#ifdef USE_SQLITE
|
||||
char* dbFileName;
|
||||
|
@ -77,6 +79,7 @@ typedef struct LaunchParams {
|
|||
#endif
|
||||
VTableMgr* vtMgr;
|
||||
DictMgrCtxt* dictMgr;
|
||||
XW_DUtilCtxt* dutil;
|
||||
XP_U16 nLocalPlayers;
|
||||
XP_U16 nHidden;
|
||||
XP_U16 gameSeed;
|
||||
|
@ -106,6 +109,7 @@ typedef struct LaunchParams {
|
|||
XP_Bool useCurses;
|
||||
XP_Bool useUdp;
|
||||
XP_Bool useHTTP;
|
||||
XP_Bool runSMSTest;
|
||||
XP_Bool noHTTPAuto;
|
||||
XP_U16 splitPackets;
|
||||
XP_U16 chatsInterval; /* 0 means disabled */
|
||||
|
@ -150,7 +154,8 @@ typedef struct LaunchParams {
|
|||
#endif
|
||||
#ifdef XWFEATURE_SMS
|
||||
struct {
|
||||
const char* phone;
|
||||
const char* myPhone;
|
||||
const char* serverPhone;
|
||||
int port;
|
||||
} sms;
|
||||
#endif
|
||||
|
@ -197,7 +202,6 @@ struct CommonGlobals {
|
|||
XP_U16 lastStreamSize;
|
||||
XP_U16 nMissing;
|
||||
XP_Bool manualFinal; /* use asked for final scores */
|
||||
sqlite3* pDb;
|
||||
sqlite3_int64 selRow;
|
||||
|
||||
SocketAddedFunc socketAdded;
|
||||
|
|
|
@ -151,7 +151,7 @@ write_callback(void *contents, size_t size, size_t nmemb, void* data)
|
|||
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;
|
||||
const size_t newLength = size * nmemb;
|
||||
XP_ASSERT( (oldLen + newLength) > 0 );
|
||||
|
@ -332,9 +332,8 @@ relaycon_invite( LaunchParams* params, XP_U32 destDevID,
|
|||
indx += writeLong( &tmpbuf[indx], sizeof(tmpbuf) - indx, destDevID );
|
||||
}
|
||||
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(params->mpool)
|
||||
params->vtMgr, params,
|
||||
CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool)
|
||||
params->vtMgr );
|
||||
nli_saveToStream( invit, stream );
|
||||
XP_U16 len = stream_getSize( stream );
|
||||
indx += writeShort( &tmpbuf[indx], sizeof(tmpbuf) - indx, len );
|
||||
|
@ -679,9 +678,8 @@ process( RelayConStorage* storage, XP_U8* buf, ssize_t nRead )
|
|||
#endif
|
||||
getNetLong( &ptr );
|
||||
XP_U16 len = getNetShort( &ptr );
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(storage->params->mpool)
|
||||
storage->params->vtMgr, storage,
|
||||
CHANNEL_NONE, NULL );
|
||||
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(storage->params->mpool)
|
||||
storage->params->vtMgr );
|
||||
stream_putBytes( stream, ptr, len );
|
||||
NetLaunchInfo invit;
|
||||
XP_Bool success = nli_makeFromStream( &invit, stream );
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import re, os, sys, getopt, shutil, threading, requests, json, glob
|
||||
import argparse, datetime, random, signal, subprocess, time
|
||||
from shutil import rmtree
|
||||
|
||||
# LOGDIR=./$(basename $0)_logs
|
||||
# APP_NEW=""
|
||||
|
@ -155,14 +156,14 @@ def player_params(args, NLOCALS, NPLAYERS, NAME_INDX):
|
|||
def logReaderStub(dev): dev.logReaderMain()
|
||||
|
||||
class Device():
|
||||
sConnnameMap = {}
|
||||
sHasLDevIDMap = {}
|
||||
sConnNamePat = re.compile('.*got_connect_cmd: connName: "([^"]+)".*$')
|
||||
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=\'([^\']+)\'.*')
|
||||
|
||||
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.indx = indx
|
||||
self.args = args
|
||||
|
@ -176,13 +177,15 @@ class Device():
|
|||
self.nInGame = nInGame
|
||||
# runtime stuff; init now
|
||||
self.proc = None
|
||||
self.connname = None
|
||||
self.peers = peers
|
||||
self.devID = ''
|
||||
self.launchCount = 0
|
||||
self.allDone = False # when true, can be killed
|
||||
self.nTilesLeft = None
|
||||
self.nTilesLeftPool = None
|
||||
self.nTilesLeftTray = None
|
||||
self.relayID = None
|
||||
self.relaySeed = 0
|
||||
self.locked = False
|
||||
|
||||
with open(self.logPath, "w") as log:
|
||||
log.write('New cmdline: ' + self.app + ' ' + (' '.join([str(p) for p in self.params])))
|
||||
|
@ -198,23 +201,20 @@ class Device():
|
|||
nLines += 1
|
||||
log.write(line + os.linesep)
|
||||
|
||||
# check for connname
|
||||
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)
|
||||
self.locked = True
|
||||
|
||||
# check for game over
|
||||
if not self.gameOver:
|
||||
match = Device.sGameOverPat.match(line)
|
||||
if match: self.gameOver = True
|
||||
|
||||
# Check every line for tiles left
|
||||
match = Device.sTilesLeftPat.match(line)
|
||||
if match: self.nTilesLeft = int(match.group(1))
|
||||
# Check every line for tiles left in pool
|
||||
match = Device.sTilesLeftPoolPat.match(line)
|
||||
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:
|
||||
match = Device.sRelayIDPat.match(line)
|
||||
|
@ -222,6 +222,8 @@ class Device():
|
|||
self.relaySeed = int(match.group(1))
|
||||
self.relayID = match.group(2)
|
||||
|
||||
self.locked = False
|
||||
|
||||
# print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath);
|
||||
|
||||
def launch(self):
|
||||
|
@ -233,7 +235,6 @@ class Device():
|
|||
args += [self.app] + [str(p) for p in self.params]
|
||||
if self.devID: args.extend( ' '.split(self.devID))
|
||||
self.launchCount += 1
|
||||
# self.logStream = open(self.logPath, flag)
|
||||
self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL,
|
||||
stderr = subprocess.PIPE, universal_newlines = True)
|
||||
self.pid = self.proc.pid
|
||||
|
@ -278,6 +279,7 @@ class Device():
|
|||
shutil.move(self.db, self.args.LOGDIR + '/done')
|
||||
|
||||
def send_dead(self):
|
||||
if self.args.ADD_RELAY:
|
||||
JSON = json.dumps([{'relayID': self.relayID, 'seed': self.relaySeed}])
|
||||
url = 'http://%s/xw4/relay.py/kill' % (self.args.HOST)
|
||||
params = {'params' : JSON}
|
||||
|
@ -287,8 +289,12 @@ class Device():
|
|||
print('got exception sending to', url, params, '; is relay.py running as apache module?')
|
||||
|
||||
def getTilesCount(self):
|
||||
return {'index': self.indx, 'nTilesLeft': self.nTilesLeft,
|
||||
'launchCount': self.launchCount, 'game': self.game,
|
||||
assert not self.locked
|
||||
return {'index': self.indx,
|
||||
'nTilesLeftPool': self.nTilesLeftPool,
|
||||
'nTilesLeftTray': self.nTilesLeftTray,
|
||||
'launchCount': self.launchCount,
|
||||
'game': self.game,
|
||||
}
|
||||
|
||||
def update_ldevid(self):
|
||||
|
@ -313,17 +319,15 @@ class Device():
|
|||
|
||||
def check_game(self):
|
||||
if self.gameOver and not self.allDone:
|
||||
allDone = False
|
||||
if len(Device.sConnnameMap[self.connname]) == self.nInGame:
|
||||
allDone = True
|
||||
for dev in Device.sConnnameMap[self.connname]:
|
||||
for dev in self.peers:
|
||||
if dev == self: continue
|
||||
if not dev.gameOver:
|
||||
allDone = False
|
||||
break
|
||||
|
||||
if allDone:
|
||||
for dev in Device.sConnnameMap[self.connname]:
|
||||
for dev in self.peers:
|
||||
assert self.game == dev.game
|
||||
dev.allDone = True
|
||||
|
||||
|
@ -361,7 +365,9 @@ def build_cmds(args):
|
|||
PLAT_PARMS += ['--curses', '--close-stdin']
|
||||
|
||||
for GAME in range(1, args.NGAMES + 1):
|
||||
peers = set()
|
||||
ROOM = 'ROOM_%.3d' % (GAME % args.NROOMS)
|
||||
PHONE_BASE = '%.4d' % (GAME % args.NROOMS)
|
||||
NDEVS = pick_ndevs(args)
|
||||
LOCALS = figure_locals(args, NDEVS) # as array
|
||||
NPLAYERS = sum(LOCALS)
|
||||
|
@ -376,27 +382,27 @@ def build_cmds(args):
|
|||
DB = '{}/{:02d}_{:02d}_DB.sql3'.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 += PLAT_PARMS
|
||||
PARAMS += BOARD_SIZE + ['--room', ROOM, '--trade-pct', args.TRADE_PCT, '--sort-tiles']
|
||||
if args.UNDO_PCT > 0:
|
||||
PARAMS += ['--undo-pct', args.UNDO_PCT]
|
||||
PARAMS += [ '--game-dict', DICT, '--relay-port', args.PORT, '--host', args.HOST]
|
||||
PARAMS += ['--slow-robot', '1:3', '--skip-confirm']
|
||||
PARAMS += ['--db', DB]
|
||||
PARAMS += ['--board-size', '15', '--trade-pct', args.TRADE_PCT, '--sort-tiles']
|
||||
|
||||
# We SHOULD support having both SMS and relay working...
|
||||
if args.ADD_RELAY:
|
||||
PARAMS += [ '--relay-port', args.PORT, '--room', ROOM, '--host', args.HOST]
|
||||
if random.randint(0,100) % 100 < g_UDP_PCT_START:
|
||||
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]
|
||||
if random.randint(0, 100) < args.HTTP_PCT:
|
||||
|
@ -424,224 +430,14 @@ def build_cmds(args):
|
|||
|
||||
# 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()
|
||||
devs.append(dev)
|
||||
|
||||
COUNTER += 1
|
||||
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):
|
||||
global gDeadLaunches
|
||||
shouldGoOn = True
|
||||
|
@ -675,13 +471,22 @@ def summarizeTileCounts(devs, endTime, state):
|
|||
nLaunches += launchCount
|
||||
fmtData[1]['data'].append('{:{width}d}'.format(launchCount, width=colWidth))
|
||||
|
||||
nTiles = datum['nTilesLeft']
|
||||
fmtData[2]['data'].append(nTiles is None and ('-' * colWidth) or '{:{width}d}'.format(nTiles, width=colWidth))
|
||||
if not nTiles is None: totalTiles += int(nTiles)
|
||||
|
||||
# Format tiles left. It's the number in the bag/pool until
|
||||
# that drops to 0, then the number in the tray preceeded by
|
||||
# '+'. 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('devs left: {}; tiles left: {}; total launches: {}; {}/{}'
|
||||
print('devs left: {}; bag tiles left: {}; total launches: {}; {}/{}'
|
||||
.format(nDevs, totalTiles, nLaunches, datetime.datetime.now(), endTime ))
|
||||
fmt = '{head:>%d} {data}' % headWidth
|
||||
for datum in fmtData: datum['data'] = ' '.join(datum['data'])
|
||||
|
@ -708,19 +513,21 @@ gDone = False
|
|||
def run_cmds(args, devs):
|
||||
nCores = countCores()
|
||||
endTime = datetime.datetime.now() + datetime.timedelta(minutes = args.TIMEOUT_MINS)
|
||||
LOOPCOUNT = 0
|
||||
printState = {}
|
||||
lastPrint = datetime.datetime.now()
|
||||
|
||||
while len(devs) > 0 and not gDone:
|
||||
if countCores() > nCores:
|
||||
print('core file count increased; exiting')
|
||||
break
|
||||
if datetime.datetime.now() > endTime:
|
||||
now = datetime.datetime.now()
|
||||
if now > endTime:
|
||||
print('outta time; outta here')
|
||||
break
|
||||
|
||||
LOOPCOUNT += 1
|
||||
if 0 == LOOPCOUNT % 20:
|
||||
# print stats every 5 seconds
|
||||
if now - lastPrint > datetime.timedelta(seconds = 5):
|
||||
lastPrint = now
|
||||
if not summarizeTileCounts(devs, endTime, printState):
|
||||
print('no change in too long; exiting')
|
||||
break
|
||||
|
@ -742,20 +549,16 @@ def run_cmds(args, devs):
|
|||
# PIDS[$KEY]=$PID
|
||||
# ROOM_PIDS[$ROOM]=$PID
|
||||
# MINEND[$KEY]=$(($NOW + $MINRUN))
|
||||
elif not dev.minTimeExpired():
|
||||
# print('sleeping...')
|
||||
time.sleep(1.0)
|
||||
else:
|
||||
elif dev.minTimeExpired():
|
||||
dev.kill()
|
||||
if dev.handleAllDone():
|
||||
devs.remove(dev)
|
||||
# if g_DROP_N >= 0: dev.increment_drop()
|
||||
# update_ldevid $KEY
|
||||
|
||||
else:
|
||||
time.sleep(1.0)
|
||||
|
||||
# if we get here via a break, kill any remaining games
|
||||
if devs:
|
||||
print('stopping %d remaining games' % (len(devs)))
|
||||
print('stopping {} remaining games'.format(len(devs)))
|
||||
for dev in devs:
|
||||
if dev.running(): dev.kill()
|
||||
|
||||
|
@ -828,6 +631,7 @@ def mkParser():
|
|||
help = 'No game will have fewer devices than this')
|
||||
parser.add_argument('--max-devs', dest = 'MAXDEVS', type = int, default = 4,
|
||||
help = 'No game will have more devices than this')
|
||||
|
||||
parser.add_argument('--min-run', dest = 'MINRUN', type = int, default = 2,
|
||||
help = 'Keep each run alive at least this many seconds')
|
||||
# # 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('--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,
|
||||
action = 'store_true')
|
||||
|
||||
|
@ -1058,6 +866,10 @@ def main():
|
|||
signal.signal(signal.SIGINT, termHandler)
|
||||
|
||||
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)
|
||||
nDevs = len(devs)
|
||||
run_cmds(args, devs)
|
||||
|
|
|
@ -1135,7 +1135,7 @@ DBMgr::StoreMessage( const char* const connName, int destHid,
|
|||
|
||||
void
|
||||
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;
|
||||
if ( useB64 ) {
|
||||
|
@ -1146,22 +1146,19 @@ DBMgr::decodeMessage( PGresult* result, bool useB64, int rowIndx, int b64indx,
|
|||
from = PQgetvalue( result, rowIndx, byteaIndex );
|
||||
}
|
||||
|
||||
size_t to_length;
|
||||
if ( useB64 ) {
|
||||
gsize out_len;
|
||||
guchar* txt = g_base64_decode( (const gchar*)from, &out_len );
|
||||
to_length = out_len;
|
||||
assert( to_length <= *buflen );
|
||||
memcpy( buf, txt, to_length );
|
||||
buf.insert( buf.end(), txt, txt + out_len );
|
||||
assert( buf.size() == out_len );
|
||||
g_free( txt );
|
||||
} else {
|
||||
uint8_t* bytes = PQunescapeBytea( (const uint8_t*)from,
|
||||
&to_length );
|
||||
assert( to_length <= *buflen );
|
||||
memcpy( buf, bytes, to_length );
|
||||
size_t to_length;
|
||||
uint8_t* bytes = PQunescapeBytea( (const uint8_t*)from, &to_length );
|
||||
buf.insert( buf.end(), bytes, bytes + to_length );
|
||||
assert( buf.size() == to_length );
|
||||
PQfreemem( bytes );
|
||||
}
|
||||
*buflen = to_length;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1193,12 +1190,9 @@ DBMgr::storedMessagesImpl( string test, vector<DBMgr::MsgInfo>& msgs,
|
|||
bool hasConnname = connname != NULL && '\0' != connname[0];
|
||||
MsgInfo msg( id, token, hasConnname );
|
||||
|
||||
uint8_t buf[1024];
|
||||
size_t buflen = sizeof(buf);
|
||||
decodeMessage( result, m_useB64, ii, 1, 2, buf, &buflen );
|
||||
decodeMessage( result, m_useB64, ii, 1, 2, msg.msg );
|
||||
size_t msglen = atoi( PQgetvalue( result, ii, 3 ) );
|
||||
assert( 0 == msglen || buflen == msglen );
|
||||
msg.msg.insert( msg.msg.end(), buf, &buf[buflen] );
|
||||
assert( 0 == msglen || msg.msg.size() == msglen );
|
||||
msgs.push_back( msg );
|
||||
}
|
||||
PQclear( result );
|
||||
|
|
|
@ -166,7 +166,8 @@ class DBMgr {
|
|||
int getCountWhere( const char* table, string& test );
|
||||
void RemoveStoredMessages( string& msgIDs );
|
||||
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,
|
||||
bool nullConnnameOK );
|
||||
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 =
|
||||
m_devAddrMap.insert( pair<DevIDRelay,UDPAddrRec>( devid, rec ) );
|
||||
if ( !result.second ) {
|
||||
logf( XW_LOGINFO, "%s: replacing address for %d; was %s, now %s",
|
||||
__func__, devid, result.first->second, rec );
|
||||
logf( XW_LOGINFO, "%s: replacing address for %d", __func__, devid );
|
||||
result.first->second = rec;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,16 +35,16 @@ UDPAckTrack::nextPacketID( XWRelayReg cmd )
|
|||
{
|
||||
uint32_t result = 0;
|
||||
if ( shouldAck( cmd ) ) {
|
||||
result = get()->nextPacketIDImpl();
|
||||
result = get()->nextPacketIDImpl( cmd );
|
||||
assert( PACKETID_NONE != result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static*/ void
|
||||
/* static*/ string
|
||||
UDPAckTrack::recordAck( uint32_t packetID )
|
||||
{
|
||||
get()->recordAckImpl( packetID );
|
||||
return get()->recordAckImpl( packetID );
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -97,33 +97,37 @@ UDPAckTrack::ackLimit()
|
|||
}
|
||||
|
||||
uint32_t
|
||||
UDPAckTrack::nextPacketIDImpl()
|
||||
UDPAckTrack::nextPacketIDImpl( XWRelayReg cmd )
|
||||
{
|
||||
MutexLock ml( &m_mutex );
|
||||
uint32_t result = ++m_nextID;
|
||||
AckRecord record;
|
||||
AckRecord record( cmd , result );
|
||||
m_pendings.insert( pair<uint32_t,AckRecord>(result, record) );
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
string
|
||||
UDPAckTrack::recordAckImpl( uint32_t packetID )
|
||||
{
|
||||
string str;
|
||||
map<uint32_t, AckRecord>::iterator iter;
|
||||
MutexLock ml( &m_mutex );
|
||||
iter = m_pendings.find( packetID );
|
||||
if ( m_pendings.end() == iter ) {
|
||||
logf( XW_LOGERROR, "%s: packet ID %d not found", __func__, packetID );
|
||||
} 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 ) {
|
||||
logf( XW_LOGERROR, "%s: packet ID %d took %d seconds to get acked",
|
||||
__func__, packetID, took );
|
||||
logf( XW_LOGERROR, "%s: packet %s took %d seconds to get acked",
|
||||
__func__, str.c_str(), took );
|
||||
}
|
||||
|
||||
callProc( iter, true );
|
||||
m_pendings.erase( iter );
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -134,8 +138,8 @@ UDPAckTrack::setOnAckImpl( OnAckProc proc, uint32_t packetID, void* data )
|
|||
MutexLock ml( &m_mutex );
|
||||
map<uint32_t, AckRecord>::iterator iter = m_pendings.find( packetID );
|
||||
if ( m_pendings.end() != iter ) {
|
||||
iter->second.proc = proc;
|
||||
iter->second.data = data;
|
||||
iter->second.m_proc = proc;
|
||||
iter->second.m_data = data;
|
||||
}
|
||||
}
|
||||
return canAdd;
|
||||
|
@ -180,12 +184,12 @@ void
|
|||
UDPAckTrack::callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked )
|
||||
{
|
||||
const AckRecord* record = &(iter->second);
|
||||
OnAckProc proc = record->proc;
|
||||
OnAckProc proc = record->m_proc;
|
||||
if ( NULL != proc ) {
|
||||
uint32_t packetID = iter->first;
|
||||
logf( XW_LOGINFO, "%s(packetID=%d, acked=%d, proc=%p)", __func__,
|
||||
packetID, acked, proc );
|
||||
(*proc)( acked, packetID, record->data );
|
||||
(*proc)( acked, packetID, record->m_data );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,15 +199,16 @@ UDPAckTrack::threadProc()
|
|||
for ( ; ; ) {
|
||||
time_t limit = ackLimit();
|
||||
sleep( limit / 2 );
|
||||
vector<uint32_t> older;
|
||||
vector<string> older;
|
||||
{
|
||||
MutexLock ml( &m_mutex );
|
||||
time_t now = time( NULL );
|
||||
map<uint32_t, AckRecord>::iterator 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 ) {
|
||||
older.push_back( iter->first );
|
||||
older.push_back( rec.toStr() );
|
||||
callProc( iter, false );
|
||||
m_pendings.erase( iter++ );
|
||||
} else {
|
||||
|
@ -212,14 +217,14 @@ UDPAckTrack::threadProc()
|
|||
}
|
||||
}
|
||||
if ( 0 < older.size() ) {
|
||||
StrWPF leaked;
|
||||
vector<uint32_t>::const_iterator iter = older.begin();
|
||||
string leaked;
|
||||
vector<string>::const_iterator iter = older.begin();
|
||||
for ( ; ; ) {
|
||||
leaked.catf( "%d", *iter );
|
||||
leaked += iter->c_str();
|
||||
if ( ++iter == older.end() ) {
|
||||
break;
|
||||
}
|
||||
leaked.catf( ", " );
|
||||
leaked += ", ";
|
||||
}
|
||||
logf( XW_LOGERROR, "%s: these packets leaked (were not ack'd "
|
||||
"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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -20,6 +21,8 @@
|
|||
#ifndef _UDPACK_H_
|
||||
#define _UDPACK_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "xwrelay_priv.h"
|
||||
#include "xwrelay.h"
|
||||
#include "strwpf.h"
|
||||
|
@ -28,10 +31,26 @@ typedef void (*OnAckProc)( bool acked, uint32_t packetID, void* data );
|
|||
|
||||
class AckRecord {
|
||||
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;
|
||||
OnAckProc proc;
|
||||
void* data;
|
||||
OnAckProc m_proc;
|
||||
XWRelayReg m_cmd;
|
||||
void* m_data;
|
||||
private:
|
||||
uint32_t m_id;
|
||||
};
|
||||
|
||||
class UDPAckTrack {
|
||||
|
@ -39,7 +58,7 @@ class UDPAckTrack {
|
|||
static const uint32_t PACKETID_NONE = 0;
|
||||
|
||||
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 shouldAck( XWRelayReg cmd );
|
||||
/* called from ctrl port */
|
||||
|
@ -51,8 +70,8 @@ class UDPAckTrack {
|
|||
static void* thread_main( void* arg );
|
||||
UDPAckTrack();
|
||||
time_t ackLimit();
|
||||
uint32_t nextPacketIDImpl();
|
||||
void recordAckImpl( uint32_t packetID );
|
||||
uint32_t nextPacketIDImpl( XWRelayReg cmd );
|
||||
string recordAckImpl( uint32_t packetID );
|
||||
bool setOnAckImpl( OnAckProc proc, uint32_t packetID, void* data );
|
||||
void callProc( const map<uint32_t, AckRecord>::iterator iter, bool acked );
|
||||
void printAcksImpl( StrWPF& out );
|
||||
|
|
|
@ -1702,7 +1702,7 @@ retrieveMessages( DevID& devID, const AddrInfo* addr )
|
|||
}
|
||||
}
|
||||
|
||||
static const char*
|
||||
const char*
|
||||
msgToStr( XWRelayReg msg )
|
||||
{
|
||||
const char* str;
|
||||
|
@ -1840,7 +1840,7 @@ handle_udp_packet( PacketThreadClosure* ptc )
|
|||
}
|
||||
|
||||
case XWPDEV_KEEPALIVE:
|
||||
case XWPDEV_RQSTMSGS: { // here
|
||||
case XWPDEV_RQSTMSGS: {
|
||||
DevID devID( ID_TYPE_RELAY );
|
||||
if ( getVLIString( &ptr, end, devID.m_devIDString ) ) {
|
||||
const AddrInfo* addr = ptc->addr();
|
||||
|
@ -1855,8 +1855,8 @@ handle_udp_packet( PacketThreadClosure* ptc )
|
|||
case XWPDEV_ACK: {
|
||||
uint32_t packetID;
|
||||
if ( vli2un( &ptr, end, &packetID ) ) {
|
||||
logf( XW_LOGINFO, "%s: got ack for packet %d", __func__, packetID );
|
||||
UDPAckTrack::recordAck( packetID );
|
||||
string str = UDPAckTrack::recordAck( packetID );
|
||||
logf( XW_LOGINFO, "%s: got ack for packet %s", __func__, str.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ int read_packet( int sock, uint8_t* buf, int buflen );
|
|||
void onMsgAcked( bool acked, uint32_t packetID, void* data );
|
||||
|
||||
const char* cmdToStr( XWRELAY_Cmd cmd );
|
||||
const char* msgToStr( XWRelayReg msg );
|
||||
|
||||
extern class ListenerMgr g_listeners;
|
||||
|
||||
|
|
Loading…
Reference in a new issue