Merge branch 'android_branch' into android_translate

This commit is contained in:
Eric House 2018-07-18 22:30:58 -07:00
commit a3fb82ebc4
84 changed files with 3187 additions and 1672 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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"

View file

@ -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

View file

@ -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:

View file

@ -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 ) ) {

View file

@ -151,14 +151,16 @@ 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 )
{
String name = removeDictExtn( new File(path).getName() );
if ( map.containsKey( name ) ) {
Log.d( TAG, "replacing info for %s with from %s", name, 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 ) );
}
map.put( name, new DictAndLoc( name, loc ) );
}
private static void tryDir( Context context, File dir, boolean strict,
@ -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;
}

View file

@ -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;
}
}

View file

@ -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,
ResendDoneProc proc )
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 );
}
});
}
}
}

View file

@ -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 );

View file

@ -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;

View file

@ -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 ) ) {
nSent = bytes.length;
}
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 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 );
result[ii] = part;
start = end;
}
} else {
Log.w( TAG, "breakAndEncode(): msg count %d too large; dropping",
count );
int[] waitSecs = { 0 };
byte[][] msgs = XwJNI.smsproto_prepOutbound( data, phone, forceNow, waitSecs );
if ( null != msgs ) {
sendBuffers( msgs, phone );
}
return result;
if ( waitSecs[0] > 0 ) {
Assert.assertFalse( forceNow );
postResend( phone, waitSecs[0] );
}
}
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 );
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() );
}
}
} ).start();
}
private void receive( SMS_CMD cmd, byte[] data, String phone )
@ -534,51 +531,18 @@ 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 ) ) {
postEvent( MultiEvent.SMS_RECEIVE_OK );
} else {
// Will see this when don't have SMS permission
Log.w( TAG, "receiveBuffer(): bogus 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 );
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 {
success = false;
Log.w( TAG, "receiveBuffer(): bogus or incomplete message from phone %s",
senderPhone );
}
return success;
}
private boolean disAssemble( String senderPhone, byte[] fullMsg )
@ -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;
}
}
}

View file

@ -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 );

View file

@ -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 );

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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 );

View file

@ -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,24 +133,28 @@ public class JNIUtilsImpl implements JNIUtils {
public String getMD5SumFor( byte[] bytes )
{
byte[] digest = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buf = new byte[128];
int nLeft = bytes.length;
int offset = 0;
while ( 0 < nLeft ) {
int len = Math.min( buf.length, nLeft );
System.arraycopy( bytes, offset, buf, 0, len );
md.update( buf, 0, len );
nLeft -= len;
offset += len;
String result = null;
if ( bytes != null ) {
byte[] digest = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buf = new byte[128];
int nLeft = bytes.length;
int offset = 0;
while ( 0 < nLeft ) {
int len = Math.min( buf.length, nLeft );
System.arraycopy( bytes, offset, buf, 0, len );
md.update( buf, 0, len );
nLeft -= len;
offset += len;
}
digest = md.digest();
} catch ( java.security.NoSuchAlgorithmException nsae ) {
Log.ex( TAG, nsae );
}
digest = md.digest();
} catch ( java.security.NoSuchAlgorithmException nsae ) {
Log.ex( TAG, nsae );
result = Utils.digestToString( digest );
}
return Utils.digestToString( digest );
return result;
}
public String getMD5SumFor( String dictName, byte[] bytes )

View file

@ -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 );
}

View file

@ -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 );
}

View file

@ -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 );
}

View file

@ -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>

View file

@ -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>

View 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>

View file

@ -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"

View file

@ -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)

View file

@ -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;

View file

@ -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 );

View file

@ -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;

View file

@ -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 );
@ -82,6 +81,14 @@ and_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
XP_LOGF( "%s: skipping call into java because jutil==NULL", \
__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,
@ -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,
const XP_UCHAR* p2 )
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",
"(Ljava/lang/String;Ljava/lang/String;)Z" );
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;
}
@ -576,61 +654,61 @@ and_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr,
}
static void
and_util_setIsServer(XW_UtilCtxt* uc, XP_Bool isServer )
and_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
{
/* Change both the C and Java structs, which need to stay in sync */
uc->gameInfo->serverRole = isServer? SERVER_ISSERVER : SERVER_ISCLIENT;
UTIL_CBK_HEADER("setIsServer", "(Z)V" );
UTIL_CBK_HEADER( "setIsServer", "(Z)V" );
(*env)->CallVoidMethod( env, util->jutil, mid, isServer );
UTIL_CBK_TAIL();
}
#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,
const XP_UCHAR* idRelay )
and_dutil_deviceRegistered( XW_DUtilCtxt* duc, DevIDType typ,
const XP_UCHAR* idRelay )
{
UTIL_CBK_HEADER( "deviceRegistered",
"(L" PKG_PATH("jni/UtilCtxt$DevIDType") ";Ljava/lang/String;)V" );
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;
}
@ -681,8 +766,8 @@ 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 )
makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
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;
}

View file

@ -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 );

View file

@ -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
@ -157,7 +183,7 @@ static void
map_init( MPFORMAL EnvThreadInfo* ti, JNIEnv* env )
{
pthread_mutex_init( &ti->mtxThreads, NULL );
MPASSIGN(ti->mpool, mpool);
MPASSIGN( ti->mpool, mpool );
map_thread( ti, env );
}
@ -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,28 +579,24 @@ 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) );
if ( game_makeFromStream( MPPARM(mpool) stream, NULL,
if ( game_makeFromStream( MPPARM(mpool) stream, NULL,
&gi, NULL, NULL, NULL, NULL, NULL, NULL ) ) {
setJGI( env, jgi, &gi );
} else {
@ -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 );

View file

@ -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

View file

@ -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

View file

@ -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,12 +1091,11 @@ 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,
STR_COMMIT_CONFIRM);
const XP_UCHAR* str = dutil_getUserString( board->dutil,
STR_COMMIT_CONFIRM );
stream_catString( stream, str );
XP_Bool warn = board->util->gameInfo->phoniesAction == PHONIES_WARN;
@ -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 );

View file

@ -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;

View file

@ -139,6 +139,7 @@ struct BoardCtxt {
ModelCtxt* model;
ServerCtxt* server;
DrawCtx* draw;
XW_DUtilCtxt* dutil;
XW_UtilCtxt* util;
struct CurGameInfo* gi;

View file

@ -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,8 +1173,8 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
}
#ifdef COMMS_CHECKSUM
newMsgElem->checksum = util_md5sum( comms->util, newMsgElem->msg,
newMsgElem->len );
newMsgElem->checksum = dutil_md5sum( comms->dutil, newMsgElem->msg,
newMsgElem->len );
#endif
return newMsgElem;
} /* makeElemWithID */
@ -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,11 +1981,14 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
break;
case COMMS_CONN_SMS:
#ifdef XWFEATURE_SMS
if ( util_phoneNumbersSame( comms->util, addr->u.sms.phone,
rec->addr.u.sms.phone )
&& addr->u.sms.port == rec->addr.u.sms.port ) {
matched = XP_TRUE;
XP_ASSERT( 0 );
{
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;
@ -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 ) {

View file

@ -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

View file

@ -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)

View file

@ -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
View 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

View file

@ -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;
}

View file

@ -258,20 +258,27 @@ void*
mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
const char* func, XP_U32 lineNo )
{
MemPoolEntry* entry = findEntryFor( mpool, ptr, (MemPoolEntry**)NULL );
if ( !entry ) {
XP_LOGF( "findEntryFor failed; called from %s, line %d",
file, 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 {
entry->ptr = XP_PLATREALLOC( entry->ptr, newsize );
XP_ASSERT( !!entry->ptr );
entry->fileName = file;
entry->func = func;
entry->lineNo = lineNo;
entry->size = newsize;
MemPoolEntry* entry = findEntryFor( mpool, ptr, (MemPoolEntry**)NULL );
if ( !entry ) {
XP_LOGF( "findEntryFor failed; called from %s, line %d",
file, lineNo );
} else {
entry->ptr = XP_PLATREALLOC( entry->ptr, newsize );
XP_ASSERT( !!entry->ptr );
entry->fileName = file;
entry->func = func;
entry->lineNo = lineNo;
entry->size = newsize;
}
result = entry->ptr;
}
return entry->ptr;
return result;
} /* mpool_realloc */
void

View file

@ -67,6 +67,12 @@ static StreamCtxVTable* make_vtable( MemStreamCtxt* stream );
* 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 )
{

View file

@ -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

View file

@ -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 );

View file

@ -51,6 +51,7 @@ typedef struct _RecordWordsInfo {
} RecordWordsInfo;
typedef struct ModelVolatiles {
XW_DUtilCtxt* dutil;
XW_UtilCtxt* util;
struct CurGameInfo* gi;
DictionaryCtxt* dict;

View file

@ -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;
}

View file

@ -513,8 +513,8 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
score += EMPTIED_TRAY_BONUS;
if ( !!stream ) {
const XP_UCHAR* bstr = util_getUserString( model->vol.util,
STR_BONUS_ALL );
const XP_UCHAR* bstr = dutil_getUserString( model->vol.dutil,
STR_BONUS_ALL );
stream_catString( stream, bstr );
}
}
@ -844,9 +844,9 @@ static void
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),
score);
XP_SNPRINTF( buf, sizeof(buf),
dutil_getUserString(model->vol.dutil, STRD_TURN_SCORE),
score );
XP_ASSERT( XP_STRLEN(buf) < sizeof(buf) );
stream_catString( stream, buf );
} /* formatSummary */

View file

@ -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 */

View file

@ -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,8 +917,8 @@ makeRobotMove( ServerCtxt* server )
if ( !!stream ) {
XP_UCHAR buf[64];
str = util_getUserQuantityString( util, STRD_ROBOT_TRADED,
MAX_TRAY_TILES );
str = dutil_getUserQuantityString( dutil, STRD_ROBOT_TRADED,
MAX_TRAY_TILES );
XP_SNPRINTF( buf, sizeof(buf), str, MAX_TRAY_TILES );
stream_catString( stream, buf );
@ -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,8 +2244,8 @@ 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,
tradedTiles->nTiles );
dutil_getUserQuantityString( server->vol.dutil, STRD_ROBOT_TRADED,
tradedTiles->nTiles );
XP_SNPRINTF( tradeBuf, sizeof(tradeBuf), tradeStr,
tradedTiles->nTiles );
stream = mkServerStream( server );
@ -2503,6 +2504,9 @@ server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
} else {
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,8 +3029,8 @@ 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,
STRS_VALUES_HEADER );
const XP_UCHAR* fmt = dutil_getUserString( server->vol.dutil,
STRS_VALUES_HEADER );
const XP_UCHAR* langName;
XP_ASSERT( !!server->vol.model );
@ -3096,9 +3100,9 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
XP_ASSERT( !!server->vol.model );
const XP_UCHAR* fmt = util_getUserQuantityString( server->vol.util,
STRD_REMAINS_HEADER,
nLeft );
const XP_UCHAR* fmt = dutil_getUserQuantityString( server->vol.dutil,
STRD_REMAINS_HEADER,
nLeft );
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
stream_catString( stream, buf );
stream_catString( stream, "\n\n" );
@ -3136,8 +3140,8 @@ server_formatRemainingTiles( ServerCtxt* server, XWStreamCtxt* stream,
XP_ASSERT( offset < sizeof(cntsBuf) );
}
fmt = util_getUserQuantityString( server->vol.util, STRD_REMAINS_EXPL,
nLeft );
fmt = dutil_getUserQuantityString( server->vol.dutil, STRD_REMAINS_EXPL,
nLeft );
XP_SNPRINTF( buf, sizeof(buf), fmt, nLeft );
stream_catString( stream, buf );
@ -3226,10 +3230,10 @@ 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,
STRD_REMAINING_TILES_ADD );
const XP_UCHAR* subString = util_getUserString( server->vol.util,
STRD_UNUSED_TILES_SUB );
const XP_UCHAR* addString = dutil_getUserString( server->vol.dutil,
STRD_REMAINING_TILES_ADD );
const XP_UCHAR* subString = dutil_getUserString( server->vol.dutil,
STRD_UNUSED_TILES_SUB );
XP_UCHAR* timeStr;
CurGameInfo* gi = server->vol.gi;
const XP_U16 nPlayers = gi->nPlayers;
@ -3279,9 +3283,8 @@ 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,
STRD_TIME_PENALTY_SUB ),
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] );

View file

@ -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
View 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
View 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

View file

@ -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;
}
/*****************************************************************************
*
****************************************************************************/

View file

@ -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

View file

@ -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

View file

@ -8,3 +8,4 @@ discon_ok2.sh_logs
test_backsend.sh_*
*.db
dawg2dict
discon_ok2.py_logs

View file

@ -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)

View file

@ -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 );

View file

@ -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,10 +512,10 @@ 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')",
key, value );
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 );
assertPrintResult( pDb, result, SQLITE_OK );
@ -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 );
} else {
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

View file

@ -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

View file

@ -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 );

View file

@ -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 );

View file

@ -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 );

View file

@ -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, &params->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
View 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
View 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

View file

@ -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,12 +936,12 @@ 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,
sizeof(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,
sizeof(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;
} else if ( !params->noAnonDevid ) {
@ -947,8 +955,8 @@ void
linux_doInitialReg( LaunchParams* params, XP_Bool idIsNew )
{
gchar rDevIDBuf[64];
if ( !db_fetch( params->pDb, KEY_RDEVID, rDevIDBuf,
sizeof(rDevIDBuf) ) ) {
if ( !db_fetch_safe( params->pDb, KEY_RDEVID, rDevIDBuf,
sizeof(rDevIDBuf) ) ) {
rDevIDBuf[0] = '\0';
}
DevIDType typ = ID_TYPE_NONE;
@ -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( &params->dutil );
dmgr_destroy( params->dictMgr );
gi_disposePlayerInfo( MPPARM(params->mpool) &params->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;

View file

@ -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 );

View file

@ -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,58 +114,74 @@ unlock_queue( LinSMSData* storage )
}
static XP_S16
send_sms( LinSMSData* storage, XWStreamCtxt* stream,
const XP_UCHAR* phone, XP_U16 port )
write_fake_sms( LaunchParams* params, XWStreamCtxt* stream,
const XP_UCHAR* phone, XP_U16 port )
{
const XP_U8* buf = stream_getPtr( stream );
XP_U16 buflen = stream_getSize( stream );
XP_S16 nSent;
XP_U16 pct = XP_RANDOM() % 100;
XP_Bool skipWrite = pct < params->smsSendFailPct;
XP_S16 nSent = -1;
XP_ASSERT( !!storage );
char path[256];
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 );
lock_queue( storage );
XP_ASSERT( !!storage );
char path[256];
lock_queue( storage );
#ifdef DEBUG
gchar* str64 = g_base64_encode( buf, buflen );
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 );
g_mkdir_with_parents( path, 0777 ); /* just in case */
int len = strlen( path );
snprintf( &path[len], sizeof(path)-len, "/%d", count );
formatQueuePath( phone, port, path, sizeof(path) );
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) );
/* 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, "/%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 ) );
g_free( str64 );
XP_ASSERT( !strcmp( str64, sms ) );
g_free( str64 );
XP_U8 testout[buflen];
XP_U16 lenout = sizeof( testout );
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
XP_ASSERT( lenout == buflen );
XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) );
XP_U8 testout[buflen];
XP_U16 lenout = sizeof( testout );
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
XP_ASSERT( lenout == buflen );
// valgrind doesn't like this; punting on figuring out
// XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) );
#endif
FILE* fp = fopen( path, "w" );
XP_ASSERT( !!fp );
(void)fprintf( fp, ADDR_FMT, storage->myPhone, storage->myPort );
(void)fprintf( fp, "%s\n", sms );
fclose( fp );
sync();
FILE* fp = fopen( path, "w" );
XP_ASSERT( !!fp );
(void)fprintf( fp, ADDR_FMT, storage->myPhone, storage->myPort );
(void)fprintf( fp, "%s\n", sms );
fclose( fp );
sync();
unlock_queue( storage );
unlock_queue( storage );
nSent = buflen;
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 {
@ -226,7 +255,7 @@ dispatch_invite( LinSMSData* storage, XP_U16 XP_UNUSED(proto),
addrFromStream( addr, stream );
(*storage->procs->inviteReceived)( storage->procClosure, gameName,
gameID, dictLang, dictName, nPlayers,
gameID, dictLang, dictName, nPlayers,
nMissing, forceChannel, addr );
}
@ -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,
data, len );
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, &params->smsStorage );
}

View file

@ -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' );

View file

@ -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

View file

@ -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;

View file

@ -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 );

View file

@ -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,17 +279,22 @@ class Device():
shutil.move(self.db, self.args.LOGDIR + '/done')
def send_dead(self):
JSON = json.dumps([{'relayID': self.relayID, 'seed': self.relaySeed}])
url = 'http://%s/xw4/relay.py/kill' % (self.args.HOST)
params = {'params' : JSON}
try:
req = requests.get(url, params = params) # failing
except requests.exceptions.ConnectionError:
print('got exception sending to', url, params, '; is relay.py running as apache module?')
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}
try:
req = requests.get(url, params = params) # failing
except requests.exceptions.ConnectionError:
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,19 +319,17 @@ 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]:
if dev == self: continue
if not dev.gameOver:
allDone = False
break
allDone = True
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]:
assert self.game == dev.game
dev.allDone = True
if allDone:
for dev in self.peers:
assert self.game == dev.game
dev.allDone = True
# print('Closing', self.connname, datetime.datetime.now())
# for dev in Device.sConnnameMap[self.connname]:
@ -360,8 +364,10 @@ def build_cmds(args):
if not args.USE_GTK:
PLAT_PARMS += ['--curses', '--close-stdin']
for GAME in range(1, args.NGAMES + 1):
for GAME in range(1, args.NGAMES + 1):
peers = set()
ROOM = 'ROOM_%.3d' % (GAME % args.NROOMS)
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']
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, '--relay-port', args.PORT, '--host', args.HOST]
PARAMS += [ '--game-dict', DICT]
PARAMS += ['--slow-robot', '1:3', '--skip-confirm']
PARAMS += ['--db', DB]
if random.randint(0,100) % 100 < g_UDP_PCT_START:
PARAMS += ['--use-udp']
PARAMS += ['--drop-nth-packet', g_DROP_N]
if random.randint(0, 100) < args.HTTP_PCT:
@ -417,231 +423,21 @@ def build_cmds(args):
# it isn't a priority.
# PARAMS += ['--seed', args.SEED]
PARAMS += PUBLIC
if DEV > 1:
if DEV > 1:
PARAMS += ['--force-channel', DEV - 1]
else:
PARAMS += ['--server']
# 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)

View file

@ -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 );

View file

@ -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 );

View file

@ -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;
}

View file

@ -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__,

View file

@ -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 );

View file

@ -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;
}

View file

@ -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;