mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-05 20:45:49 +01:00
Merge branch 'android_branch' into android_gcm
This commit is contained in:
commit
03108f311a
30 changed files with 403 additions and 115 deletions
|
@ -22,7 +22,7 @@
|
||||||
to come from a domain that you own or have control over. -->
|
to come from a domain that you own or have control over. -->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.eehouse.android.xw4"
|
package="org.eehouse.android.xw4"
|
||||||
android:versionCode="45"
|
android:versionCode="46"
|
||||||
android:versionName="@string/app_version"
|
android:versionName="@string/app_version"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
|
@ -5,36 +5,17 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<b>Crosswords 4.4 beta 53 release</b>
|
<b>Crosswords 4.4 beta 54 release</b>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li>Add a second way devices can play: via SMS. If you and your
|
<li>Don't try to access directory OS says is for downloads when it
|
||||||
friends have unlimited texting plans (ONLY IF!!) this is the way to
|
doesn't actually exist</li>
|
||||||
go: connecting is easier, moves get transmitted more quickly, and
|
|
||||||
less battery is required. <a
|
|
||||||
href="http://xwords.sf.net/and_faq.php">See the FAQ</a></li>
|
|
||||||
|
|
||||||
<li>In the main games-list screen, replace green highlighting of the
|
|
||||||
player whose turn it is with green transitioning to red over three
|
|
||||||
days. That way you can see how long it's been that person's turn.
|
|
||||||
Nothing happens -- yet -- when the three days is up. Maybe later
|
|
||||||
that'll force a resignation.</li>
|
|
||||||
|
|
||||||
<li>Download wordlists without using the browser when possible</li>
|
|
||||||
|
|
||||||
<li>Warn when you join a network game with a different wordlist from
|
|
||||||
the host's, and give a chance to download and switch.</li>
|
|
||||||
|
|
||||||
<li>Don't allow networked games to have different wordlist for each
|
|
||||||
player. That feature is for local games only now.</li>
|
|
||||||
|
|
||||||
<li>There's now a preference for where downloaded wordlists are
|
|
||||||
stored instead of a question asked each time.</li>
|
|
||||||
|
|
||||||
<li>Recast game-over feature as "resigning".</li>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p>(The full changelog
|
||||||
|
is <a href="http://xwords.sf.net/and_changes.php">here</a>.)</p>
|
||||||
|
|
||||||
<p>Please remember that this is beta software. Please let me know (at
|
<p>Please remember that this is beta software. Please let me know (at
|
||||||
eehouse@eehouse.org) what's broken and what features you'd most like
|
eehouse@eehouse.org) what's broken and what features you'd most like
|
||||||
to see.</p>
|
to see.</p>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_version">4.4 beta 53</string>
|
<string name="app_version">4.4 beta 54</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -93,6 +93,7 @@
|
||||||
<string name="key_na_browse">key_na_browse</string>
|
<string name="key_na_browse">key_na_browse</string>
|
||||||
<string name="key_na_values">key_na_values</string>
|
<string name="key_na_values">key_na_values</string>
|
||||||
<string name="key_enable_debug">key_enable_debug</string>
|
<string name="key_enable_debug">key_enable_debug</string>
|
||||||
|
<string name="key_download_path">key_download_path</string>
|
||||||
|
|
||||||
<!-- Nor is my email address -->
|
<!-- Nor is my email address -->
|
||||||
<string name="email_author_email">xwords@eehouse.org</string>
|
<string name="email_author_email">xwords@eehouse.org</string>
|
||||||
|
|
|
@ -2123,5 +2123,5 @@
|
||||||
<string name="default_loc">Store wordlists internally</string>
|
<string name="default_loc">Store wordlists internally</string>
|
||||||
<string name="default_loc_summary">(Not in external/sdcard memory)</string>
|
<string name="default_loc_summary">(Not in external/sdcard memory)</string>
|
||||||
|
|
||||||
<string name="sms_searching_toast">Searching SMS messages...</string>
|
<string name="download_path_title">Downloads Directory</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -299,6 +299,10 @@
|
||||||
android:summary="Menuitems etc."
|
android:summary="Menuitems etc."
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
/>
|
/>
|
||||||
|
<org.eehouse.android.xw4.XWEditTextPreference
|
||||||
|
android:key="@string/key_download_path"
|
||||||
|
android:title="@string/download_path_title"
|
||||||
|
/>
|
||||||
<CheckBoxPreference android:key="@string/key_show_sms"
|
<CheckBoxPreference android:key="@string/key_show_sms"
|
||||||
android:title="Show SMS sends, receives"
|
android:title="Show SMS sends, receives"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Comparator;
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
||||||
|
import org.eehouse.android.xw4.DictUtils.DictLoc;
|
||||||
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
||||||
import org.eehouse.android.xw4.jni.XwJNI;
|
import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
import org.eehouse.android.xw4.jni.DictInfo;
|
import org.eehouse.android.xw4.jni.DictInfo;
|
||||||
|
@ -244,7 +245,7 @@ public class DictLangCache {
|
||||||
|
|
||||||
// May be called from background thread
|
// May be called from background thread
|
||||||
public static void inval( final Context context, String name,
|
public static void inval( final Context context, String name,
|
||||||
DictUtils.DictLoc loc, boolean added )
|
DictLoc loc, boolean added )
|
||||||
{
|
{
|
||||||
DictAndLoc dal = new DictAndLoc( name, loc );
|
DictAndLoc dal = new DictAndLoc( name, loc );
|
||||||
DBUtils.dictsRemoveInfo( context, dal );
|
DBUtils.dictsRemoveInfo( context, dal );
|
||||||
|
@ -372,7 +373,7 @@ public class DictLangCache {
|
||||||
{
|
{
|
||||||
DictInfo result = DBUtils.dictsGetInfo( context, name );
|
DictInfo result = DBUtils.dictsGetInfo( context, name );
|
||||||
if ( null == result ) {
|
if ( null == result ) {
|
||||||
DictUtils.DictLoc loc = DictUtils.getDictLoc( context, name );
|
DictLoc loc = DictUtils.getDictLoc( context, name );
|
||||||
result = getInfo( context, new DictAndLoc( name, loc ) );
|
result = getInfo( context, new DictAndLoc( name, loc ) );
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -397,7 +398,7 @@ public class DictLangCache {
|
||||||
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
||||||
pairs.m_paths[0],
|
pairs.m_paths[0],
|
||||||
JNIUtilsImpl.get( context ),
|
JNIUtilsImpl.get( context ),
|
||||||
DictUtils.DictLoc.DOWNLOAD == dal.loc,
|
DictLoc.DOWNLOAD == dal.loc,
|
||||||
info ) ) {
|
info ) ) {
|
||||||
|
|
||||||
info.name = dal.name;
|
info.name = dal.name;
|
||||||
|
|
|
@ -149,7 +149,8 @@ public class DictUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, al );
|
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, al );
|
||||||
tryDir( context, getDownloadDir(), true, DictLoc.DOWNLOAD, al );
|
tryDir( context, getDownloadDir( context ), true,
|
||||||
|
DictLoc.DOWNLOAD, al );
|
||||||
|
|
||||||
s_dictListCache =
|
s_dictListCache =
|
||||||
al.toArray( new DictUtils.DictAndLoc[al.size()] );
|
al.toArray( new DictUtils.DictAndLoc[al.size()] );
|
||||||
|
@ -248,7 +249,7 @@ public class DictUtils {
|
||||||
File path = null;
|
File path = null;
|
||||||
switch( loc ) {
|
switch( loc ) {
|
||||||
case DOWNLOAD:
|
case DOWNLOAD:
|
||||||
path = getDownloadsPathFor( name );
|
path = getDownloadsPathFor( context, name );
|
||||||
break;
|
break;
|
||||||
case EXTERNAL:
|
case EXTERNAL:
|
||||||
path = getSDPathFor( context, name );
|
path = getSDPathFor( context, name );
|
||||||
|
@ -305,7 +306,7 @@ public class DictUtils {
|
||||||
FileInputStream fis = null;
|
FileInputStream fis = null;
|
||||||
if ( null == fis ) {
|
if ( null == fis ) {
|
||||||
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.DOWNLOAD ) {
|
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.DOWNLOAD ) {
|
||||||
File path = getDownloadsPathFor( name );
|
File path = getDownloadsPathFor( context, name );
|
||||||
if ( null != path && path.exists() ) {
|
if ( null != path && path.exists() ) {
|
||||||
DbgUtils.logf( "loading %s from Download", name );
|
DbgUtils.logf( "loading %s from Download", name );
|
||||||
fis = new FileInputStream( path );
|
fis = new FileInputStream( path );
|
||||||
|
@ -365,7 +366,7 @@ public class DictUtils {
|
||||||
File path;
|
File path;
|
||||||
switch ( to ) {
|
switch ( to ) {
|
||||||
case DOWNLOAD:
|
case DOWNLOAD:
|
||||||
path = getDownloadsPathFor( name );
|
path = getDownloadsPathFor( context, name );
|
||||||
break;
|
break;
|
||||||
case EXTERNAL:
|
case EXTERNAL:
|
||||||
path = getSDPathFor( context, name );
|
path = getSDPathFor( context, name );
|
||||||
|
@ -560,25 +561,36 @@ public class DictUtils {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getDownloadDir()
|
public static boolean haveDownloadDir( Context context )
|
||||||
|
{
|
||||||
|
return null != getDownloadDir( context );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getDownloadDir( Context context )
|
||||||
{
|
{
|
||||||
File result = null;
|
File result = null;
|
||||||
if ( haveWriteableSD() ) {
|
if ( haveWriteableSD() ) {
|
||||||
File storage = Environment.getExternalStorageDirectory();
|
File file = null;
|
||||||
if ( null != storage ) {
|
String myPath = XWPrefs.getMyDownloadDir( context );
|
||||||
result = new File( storage.getPath(), "download/" );
|
if ( null != myPath && 0 < myPath.length() ) {
|
||||||
if ( !result.exists() ) {
|
file = new File( myPath );
|
||||||
result = null;
|
} else {
|
||||||
|
file = Environment.getExternalStorageDirectory();
|
||||||
|
if ( null != file ) {
|
||||||
|
file = new File( file, "download/" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( null != file && file.exists() && file.isDirectory() ) {
|
||||||
|
result = file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getDownloadsPathFor( String name )
|
private static File getDownloadsPathFor( Context context, String name )
|
||||||
{
|
{
|
||||||
File result = null;
|
File result = null;
|
||||||
File dir = getDownloadDir();
|
File dir = getDownloadDir( context );
|
||||||
if ( dir != null ) {
|
if ( dir != null ) {
|
||||||
result = new File( dir, name );
|
result = new File( dir, name );
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,15 +263,6 @@ public class DictsActivity extends ExpandableListActivity
|
||||||
message = Utils.format( this, R.string.move_dictf,
|
message = Utils.format( this, R.string.move_dictf,
|
||||||
m_adapter.getSelChildView().getText() );
|
m_adapter.getSelChildView().getText() );
|
||||||
|
|
||||||
String[] items = new String[3];
|
|
||||||
for ( int ii = 0; ii < 3; ++ii ) {
|
|
||||||
DictLoc loc = itemToRealLoc(ii);
|
|
||||||
if ( loc.equals( m_moveFromLoc ) ) {
|
|
||||||
m_moveFromItem = ii;
|
|
||||||
}
|
|
||||||
items[ii] = m_locNames[loc.ordinal()];
|
|
||||||
}
|
|
||||||
|
|
||||||
OnClickListener newSelLstnr =
|
OnClickListener newSelLstnr =
|
||||||
new OnClickListener() {
|
new OnClickListener() {
|
||||||
public void onClick( DialogInterface dlgi, int item ) {
|
public void onClick( DialogInterface dlgi, int item ) {
|
||||||
|
@ -307,7 +298,8 @@ public class DictsActivity extends ExpandableListActivity
|
||||||
|
|
||||||
dialog = new AlertDialog.Builder( this )
|
dialog = new AlertDialog.Builder( this )
|
||||||
.setTitle( message )
|
.setTitle( message )
|
||||||
.setSingleChoiceItems( items, m_moveFromItem, newSelLstnr )
|
.setSingleChoiceItems( makeDictDirItems(), m_moveFromItem,
|
||||||
|
newSelLstnr )
|
||||||
.setPositiveButton( R.string.button_move, lstnr )
|
.setPositiveButton( R.string.button_move, lstnr )
|
||||||
.setNegativeButton( R.string.button_cancel, null )
|
.setNegativeButton( R.string.button_cancel, null )
|
||||||
.create();
|
.create();
|
||||||
|
@ -716,6 +708,26 @@ public class DictsActivity extends ExpandableListActivity
|
||||||
XWPrefs.setClosedLangs( this, asArray );
|
XWPrefs.setClosedLangs( this, asArray );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String[] makeDictDirItems()
|
||||||
|
{
|
||||||
|
boolean showDownload = DictUtils.haveDownloadDir( this );
|
||||||
|
int nItems = showDownload ? 3 : 2;
|
||||||
|
int nextI = 0;
|
||||||
|
String[] items = new String[nItems];
|
||||||
|
for ( int ii = 0; ii < 3; ++ii ) {
|
||||||
|
DictLoc loc = itemToRealLoc(ii);
|
||||||
|
if ( !showDownload && DictLoc.DOWNLOAD == loc ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( loc.equals( m_moveFromLoc ) ) {
|
||||||
|
m_moveFromItem = nextI;
|
||||||
|
}
|
||||||
|
items[nextI++] = m_locNames[loc.ordinal()];
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
private static Intent mkDownloadIntent( Context context, String dict_url )
|
private static Intent mkDownloadIntent( Context context, String dict_url )
|
||||||
{
|
{
|
||||||
Uri uri = Uri.parse( dict_url );
|
Uri uri = Uri.parse( dict_url );
|
||||||
|
|
|
@ -208,12 +208,20 @@ public class DlgDelegate {
|
||||||
|
|
||||||
public void showConfirmThen( String msg, int posButton, int callbackID )
|
public void showConfirmThen( String msg, int posButton, int callbackID )
|
||||||
{
|
{
|
||||||
m_msg = msg;
|
// FIX ME!! Need to store data per message rather than have
|
||||||
m_posButton = posButton;
|
// assertions failing or messages dropped.
|
||||||
Assert.assertTrue( 0 != callbackID );
|
|
||||||
Assert.assertTrue( 0 == m_cbckID );
|
if ( 0 != m_cbckID ) {
|
||||||
m_cbckID = callbackID;
|
DbgUtils.logf( "showConfirmThen: busy with another message; "
|
||||||
m_activity.showDialog( CONFIRM_THEN );
|
+ "dropping \"%s\" in favor of \"%s\"",
|
||||||
|
msg, m_msg );
|
||||||
|
} else {
|
||||||
|
m_msg = msg;
|
||||||
|
m_posButton = posButton;
|
||||||
|
Assert.assertTrue( 0 != callbackID );
|
||||||
|
m_cbckID = callbackID;
|
||||||
|
m_activity.showDialog( CONFIRM_THEN );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showEmailOrSMSThen( final int callbackID )
|
public void showEmailOrSMSThen( final int callbackID )
|
||||||
|
|
|
@ -730,7 +730,6 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
DbgUtils.logf( "feedMessages=>%b", draw );
|
|
||||||
return draw;
|
return draw;
|
||||||
} // feedMessages
|
} // feedMessages
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class PrefsActivity extends PreferenceActivity
|
public class PrefsActivity extends PreferenceActivity
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
@ -40,6 +41,7 @@ public class PrefsActivity extends PreferenceActivity
|
||||||
private String m_keyLogging;
|
private String m_keyLogging;
|
||||||
private String m_smsToasting;
|
private String m_smsToasting;
|
||||||
private String m_smsEnable;
|
private String m_smsEnable;
|
||||||
|
private String m_downloadPath;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog( int id )
|
protected Dialog onCreateDialog( int id )
|
||||||
|
@ -120,6 +122,7 @@ public class PrefsActivity extends PreferenceActivity
|
||||||
m_keyLogging = getString( R.string.key_logging_on );
|
m_keyLogging = getString( R.string.key_logging_on );
|
||||||
m_smsToasting = getString( R.string.key_show_sms );
|
m_smsToasting = getString( R.string.key_show_sms );
|
||||||
m_smsEnable = getString( R.string.key_enable_sms );
|
m_smsEnable = getString( R.string.key_enable_sms );
|
||||||
|
m_downloadPath = getString( R.string.key_download_path );
|
||||||
|
|
||||||
Button button = (Button)findViewById( R.id.revert_colors );
|
Button button = (Button)findViewById( R.id.revert_colors );
|
||||||
button.setOnClickListener( new View.OnClickListener() {
|
button.setOnClickListener( new View.OnClickListener() {
|
||||||
|
@ -164,6 +167,23 @@ public class PrefsActivity extends PreferenceActivity
|
||||||
} else {
|
} else {
|
||||||
XWPrefs.setHaveCheckedSMS( this, false );
|
XWPrefs.setHaveCheckedSMS( this, false );
|
||||||
}
|
}
|
||||||
|
} else if ( key.equals( m_downloadPath ) ) {
|
||||||
|
String value = sp.getString( key, null );
|
||||||
|
if ( null != value ) {
|
||||||
|
File dir = new File( value );
|
||||||
|
String msg = null;
|
||||||
|
if ( !dir.exists() ) {
|
||||||
|
msg = String.format( "%s does not exist", value );
|
||||||
|
} else if ( !dir.isDirectory() ) {
|
||||||
|
msg = String.format( "%s is not a directory", value );
|
||||||
|
} else if ( !dir.canWrite() ) {
|
||||||
|
msg = String.format( "Cannot write to %s", value );
|
||||||
|
}
|
||||||
|
if ( null != msg ) {
|
||||||
|
Utils.showToast( this, msg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DictUtils.invalDictList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,6 @@ public class SMSService extends Service {
|
||||||
switch( cmd ) {
|
switch( cmd ) {
|
||||||
case CHECK_MSGDB:
|
case CHECK_MSGDB:
|
||||||
if ( ! XWPrefs.getHaveCheckedSMS( this ) ) {
|
if ( ! XWPrefs.getHaveCheckedSMS( this ) ) {
|
||||||
Utils.showToast( this, R.string.sms_searching_toast );
|
|
||||||
XWPrefs.setHaveCheckedSMS( this, true );
|
XWPrefs.setHaveCheckedSMS( this, true );
|
||||||
new Thread( new Runnable() {
|
new Thread( new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -216,6 +216,11 @@ public class XWPrefs {
|
||||||
: DictUtils.DictLoc.EXTERNAL;
|
: DictUtils.DictLoc.EXTERNAL;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getMyDownloadDir( Context context )
|
||||||
|
{
|
||||||
|
return getPrefsString( context, R.string.key_download_path );
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean getDefaultLocInternal( Context context )
|
public static boolean getDefaultLocInternal( Context context )
|
||||||
{
|
{
|
||||||
|
|
|
@ -1330,6 +1330,16 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
sizeof(comms->r.connName) );
|
sizeof(comms->r.connName) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_DEVID
|
||||||
|
if ( !reconnected ) {
|
||||||
|
XP_UCHAR devID[MAX_DEVID_LEN + 1];
|
||||||
|
stringFromStreamHere( stream, devID, sizeof(devID) );
|
||||||
|
if ( devID[0] != '\0' ) {
|
||||||
|
util_deviceRegistered( comms->util, devID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
(*comms->procs.rconnd)( comms->procs.closure,
|
(*comms->procs.rconnd)( comms->procs.closure,
|
||||||
comms->addr.u.ip_relay.invite, reconnected,
|
comms->addr.u.ip_relay.invite, reconnected,
|
||||||
comms->r.myHostID, XP_FALSE, nSought - nHere );
|
comms->r.myHostID, XP_FALSE, nSought - nHere );
|
||||||
|
|
|
@ -55,12 +55,6 @@ typedef enum {
|
||||||
, COMMS_RELAYSTATE_ALLCONNECTED
|
, COMMS_RELAYSTATE_ALLCONNECTED
|
||||||
} CommsRelayState;
|
} CommsRelayState;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ID_TYPE_NONE
|
|
||||||
,ID_TYPE_LINUX
|
|
||||||
,ID_TYPE_ANDROID_GCM
|
|
||||||
} DevIDType;
|
|
||||||
|
|
||||||
#ifdef XWFEATURE_BLUETOOTH
|
#ifdef XWFEATURE_BLUETOOTH
|
||||||
# define XW_BT_UUID "7be0d084-ff89-4d6d-9c78-594773a6f963"
|
# define XW_BT_UUID "7be0d084-ff89-4d6d-9c78-594773a6f963"
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,7 @@ typedef struct UtilVtable {
|
||||||
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
|
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
const XP_UCHAR* (*m_util_getDevID)( XW_UtilCtxt* uc, DevIDType* typ );
|
const XP_UCHAR* (*m_util_getDevID)( XW_UtilCtxt* uc, DevIDType* typ );
|
||||||
|
void (*m_util_deviceRegistered)( XW_UtilCtxt* uc, const XP_UCHAR* idRelay );
|
||||||
#endif
|
#endif
|
||||||
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
|
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
|
||||||
|
|
||||||
|
@ -285,6 +286,8 @@ struct XW_UtilCtxt {
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
# define util_getDevID( uc, t ) \
|
# define util_getDevID( uc, t ) \
|
||||||
(uc)->vtable->m_util_getDevID((uc),(t))
|
(uc)->vtable->m_util_getDevID((uc),(t))
|
||||||
|
# define util_deviceRegistered( uc, id ) \
|
||||||
|
(uc)->vtable->m_util_deviceRegistered( (uc), (id) )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define util_makeEmptyDict( uc ) \
|
#define util_makeEmptyDict( uc ) \
|
||||||
|
|
|
@ -505,6 +505,7 @@ typedef enum {
|
||||||
,CMD_SEED
|
,CMD_SEED
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
,CMD_DEVID
|
,CMD_DEVID
|
||||||
|
,CMD_RDEVID
|
||||||
#endif
|
#endif
|
||||||
,CMD_GAMESEED
|
,CMD_GAMESEED
|
||||||
,CMD_GAMEFILE
|
,CMD_GAMEFILE
|
||||||
|
@ -601,6 +602,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
||||||
,{ CMD_SEED, true, "seed", "random seed" }
|
,{ CMD_SEED, true, "seed", "random seed" }
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
,{ CMD_DEVID, true, "devid", "device ID (for testing GCM stuff)" }
|
,{ CMD_DEVID, true, "devid", "device ID (for testing GCM stuff)" }
|
||||||
|
,{ CMD_RDEVID, true, "rdevid", "relay's converted device ID (for testing GCM stuff)" }
|
||||||
#endif
|
#endif
|
||||||
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
|
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
|
||||||
,{ CMD_GAMEFILE, true, "file", "file to save to/read from" }
|
,{ CMD_GAMEFILE, true, "file", "file to save to/read from" }
|
||||||
|
@ -1683,6 +1685,9 @@ main( int argc, char** argv )
|
||||||
case CMD_DEVID:
|
case CMD_DEVID:
|
||||||
mainParams.devID = optarg;
|
mainParams.devID = optarg;
|
||||||
break;
|
break;
|
||||||
|
case CMD_RDEVID:
|
||||||
|
mainParams.rDevID = optarg;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case CMD_GAMESEED:
|
case CMD_GAMESEED:
|
||||||
mainParams.gameSeed = atoi(optarg);
|
mainParams.gameSeed = atoi(optarg);
|
||||||
|
|
|
@ -348,9 +348,25 @@ linux_util_getUserString( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 code )
|
||||||
static const XP_UCHAR*
|
static const XP_UCHAR*
|
||||||
linux_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
|
linux_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
|
||||||
{
|
{
|
||||||
|
XP_UCHAR* result;
|
||||||
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
||||||
*typ = ID_TYPE_LINUX;
|
if ( !!cGlobals->params->rDevID ) {
|
||||||
return cGlobals->params->devID;
|
*typ = ID_TYPE_RELAY;
|
||||||
|
result = cGlobals->params->rDevID;
|
||||||
|
} else {
|
||||||
|
*typ = ID_TYPE_LINUX;
|
||||||
|
result = cGlobals->params->devID;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linux_util_deviceRegistered( XW_UtilCtxt* XP_UNUSED(uc),
|
||||||
|
const XP_UCHAR* idRelay )
|
||||||
|
{
|
||||||
|
/* Script discon_ok2.sh is grepping for this in logs, so don't change
|
||||||
|
it! */
|
||||||
|
XP_LOGF( "%s: new id: %s", __func__, idRelay );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -365,6 +381,7 @@ linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
|
||||||
util->vtable->m_util_getUserString = linux_util_getUserString;
|
util->vtable->m_util_getUserString = linux_util_getUserString;
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
util->vtable->m_util_getDevID = linux_util_getDevID;
|
util->vtable->m_util_getDevID = linux_util_getDevID;
|
||||||
|
util->vtable->m_util_deviceRegistered = linux_util_deviceRegistered;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ typedef struct LaunchParams {
|
||||||
char* bonusFile;
|
char* bonusFile;
|
||||||
#ifdef XWFEATURE_DEVID
|
#ifdef XWFEATURE_DEVID
|
||||||
char* devID;
|
char* devID;
|
||||||
|
char* rDevID;
|
||||||
#endif
|
#endif
|
||||||
VTableMgr* vtMgr;
|
VTableMgr* vtMgr;
|
||||||
XP_U16 nLocalPlayers;
|
XP_U16 nLocalPlayers;
|
||||||
|
|
|
@ -190,6 +190,7 @@ build_cmds() {
|
||||||
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
|
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
|
||||||
# PARAMS="$PARAMS --savefail-pct 10"
|
# PARAMS="$PARAMS --savefail-pct 10"
|
||||||
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
|
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
|
||||||
|
# PARAMS="$PARAMS --devid LINUX_TEST_$(printf %.5d ${COUNTER})"
|
||||||
PARAMS="$PARAMS $PUBLIC"
|
PARAMS="$PARAMS $PUBLIC"
|
||||||
ARGS[$COUNTER]=$PARAMS
|
ARGS[$COUNTER]=$PARAMS
|
||||||
ROOMS[$COUNTER]=$ROOM
|
ROOMS[$COUNTER]=$ROOM
|
||||||
|
@ -380,6 +381,19 @@ increment_drop() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_relay_devid() {
|
||||||
|
KEY=$1
|
||||||
|
CMD=${ARGS[$KEY]}
|
||||||
|
if [ "$CMD" != "${CMD/--devid //}" ]; then
|
||||||
|
RELAY_ID=$(grep 'deviceRegistered: new id: ' ${LOGS[$KEY]} | tail -n 1)
|
||||||
|
if [ -n "$RELAY_ID" ]; then
|
||||||
|
RELAY_ID=$(echo $RELAY_ID | sed 's,^.*new id: ,,')
|
||||||
|
# turn --devid <whatever> into --rdevid $RELAY_ID
|
||||||
|
ARGS[$KEY]=$(echo $CMD | sed 's,^\(.*\)--devid[ ]\+[^ ]\+\(.*\)$,\1--rdevid $RELAY_ID\2,')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
run_cmds() {
|
run_cmds() {
|
||||||
ENDTIME=$(($(date +%s) + TIMEOUT))
|
ENDTIME=$(($(date +%s) + TIMEOUT))
|
||||||
while :; do
|
while :; do
|
||||||
|
@ -409,6 +423,7 @@ run_cmds() {
|
||||||
PIDS[$KEY]=0
|
PIDS[$KEY]=0
|
||||||
ROOM_PIDS[$ROOM]=0
|
ROOM_PIDS[$ROOM]=0
|
||||||
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
|
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
|
||||||
|
# set_relay_devid $KEY
|
||||||
check_game $KEY
|
check_game $KEY
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
@ -641,9 +642,10 @@ CookieRef::handleEvents()
|
||||||
case XWA_SEND_CONNRSP:
|
case XWA_SEND_CONNRSP:
|
||||||
{
|
{
|
||||||
HostID hid;
|
HostID hid;
|
||||||
if ( increasePlayerCounts( &evt, false, &hid ) ) {
|
DBMgr::DevIDRelay devID;
|
||||||
|
if ( increasePlayerCounts( &evt, false, &hid, &devID ) ) {
|
||||||
setAllConnectedTimer();
|
setAllConnectedTimer();
|
||||||
sendResponse( &evt, true );
|
sendResponse( &evt, true, &devID );
|
||||||
setAckTimer( hid );
|
setAckTimer( hid );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,8 +668,8 @@ CookieRef::handleEvents()
|
||||||
/* break; */
|
/* break; */
|
||||||
|
|
||||||
case XWA_SEND_RERSP:
|
case XWA_SEND_RERSP:
|
||||||
increasePlayerCounts( &evt, true, NULL );
|
increasePlayerCounts( &evt, true, NULL, NULL );
|
||||||
sendResponse( &evt, false );
|
sendResponse( &evt, false, NULL );
|
||||||
sendAnyStored( &evt );
|
sendAnyStored( &evt );
|
||||||
postCheckAllHere();
|
postCheckAllHere();
|
||||||
break;
|
break;
|
||||||
|
@ -855,8 +857,10 @@ CookieRef::send_stored_messages( HostID dest, int socket )
|
||||||
} /* send_stored_messages */
|
} /* send_stored_messages */
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
|
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
|
||||||
|
DBMgr::DevIDRelay* devIDp )
|
||||||
{
|
{
|
||||||
|
DBMgr::DevIDRelay devID = DBMgr::DEVID_NONE;
|
||||||
int nPlayersH = evt->u.con.nPlayersH;
|
int nPlayersH = evt->u.con.nPlayersH;
|
||||||
int socket = evt->u.con.socket;
|
int socket = evt->u.con.socket;
|
||||||
int seed = evt->u.con.seed;
|
int seed = evt->u.con.seed;
|
||||||
|
@ -883,10 +887,25 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
|
||||||
assert( m_nPlayersHere <= m_nPlayersSought );
|
assert( m_nPlayersHere <= m_nPlayersSought );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !!devIDp ) {
|
||||||
|
DevIDType devIDType = evt->u.con.devID->m_devIDType;
|
||||||
|
// does client support devID
|
||||||
|
if ( ID_TYPE_NONE != devIDType ) {
|
||||||
|
// have we not already converted it?
|
||||||
|
if ( ID_TYPE_RELAY == devIDType ) {
|
||||||
|
devID = (DBMgr::DevIDRelay)strtoul( evt->u.con.devID->m_devIDString.c_str(),
|
||||||
|
NULL, 16 );
|
||||||
|
} else {
|
||||||
|
devID = DBMgr::Get()->RegisterDevice( evt->u.con.devID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*devIDp = devID;
|
||||||
|
}
|
||||||
|
|
||||||
evt->u.con.srcID =
|
evt->u.con.srcID =
|
||||||
DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
|
DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
|
||||||
evt->u.con.clientVersion, nPlayersH, seed,
|
evt->u.con.clientVersion, nPlayersH, seed,
|
||||||
evt->u.con.addr, evt->u.con.devID, reconn );
|
evt->u.con.addr, devID, reconn );
|
||||||
|
|
||||||
HostID hostid = evt->u.con.srcID;
|
HostID hostid = evt->u.con.srcID;
|
||||||
if ( NULL != hidp ) {
|
if ( NULL != hidp ) {
|
||||||
|
@ -1016,7 +1035,8 @@ CookieRef::cancelAllConnectedTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
CookieRef::sendResponse( const CRefEvent* evt, bool initial,
|
||||||
|
const DBMgr::DevIDRelay* devID )
|
||||||
{
|
{
|
||||||
int socket = evt->u.con.socket;
|
int socket = evt->u.con.socket;
|
||||||
|
|
||||||
|
@ -1028,6 +1048,7 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
||||||
+ sizeof(unsigned char) /* total here */
|
+ sizeof(unsigned char) /* total here */
|
||||||
+ sizeof(unsigned char) /* total expected */
|
+ sizeof(unsigned char) /* total expected */
|
||||||
+ 1 + MAX_CONNNAME_LEN
|
+ 1 + MAX_CONNNAME_LEN
|
||||||
|
+ 1 + 1 + MAX_DEVID_LEN
|
||||||
];
|
];
|
||||||
|
|
||||||
unsigned char* bufp = buf;
|
unsigned char* bufp = buf;
|
||||||
|
@ -1043,12 +1064,33 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
||||||
|
|
||||||
const char* connName = ConnName();
|
const char* connName = ConnName();
|
||||||
assert( !!connName && connName[0] );
|
assert( !!connName && connName[0] );
|
||||||
int len = strlen( connName );
|
size_t len = strlen( connName );
|
||||||
assert( len < MAX_CONNNAME_LEN );
|
assert( len < MAX_CONNNAME_LEN );
|
||||||
*bufp++ = (char)len;
|
*bufp++ = (char)len;
|
||||||
memcpy( bufp, connName, len );
|
memcpy( bufp, connName, len );
|
||||||
bufp += len;
|
bufp += len;
|
||||||
|
|
||||||
|
if ( initial ) {
|
||||||
|
// we always write at least empty string
|
||||||
|
char idbuf[MAX_DEVID_LEN + 1] = {0};
|
||||||
|
|
||||||
|
// If client supports devid, and we have one (response case), write it as
|
||||||
|
// 8-byte hex string plus a length byte -- but only if we didn't already
|
||||||
|
// receive it.
|
||||||
|
if ( !!devID && ID_TYPE_RELAY < evt->u.con.devID->m_devIDType ) {
|
||||||
|
len = snprintf( idbuf, sizeof(idbuf), "%.8X", *devID );
|
||||||
|
assert( len < sizeof(idbuf) );
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen( idbuf );
|
||||||
|
assert( len <= MAX_DEVID_LEN );
|
||||||
|
*bufp++ = (char)len;
|
||||||
|
if ( 0 < len ) {
|
||||||
|
memcpy( bufp, idbuf, len );
|
||||||
|
bufp += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
send_with_length( socket, buf, bufp - buf, true );
|
send_with_length( socket, buf, bufp - buf, true );
|
||||||
logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) );
|
logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) );
|
||||||
} /* sendResponse */
|
} /* sendResponse */
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "xwrelay_priv.h"
|
#include "xwrelay_priv.h"
|
||||||
#include "xwrelay.h"
|
#include "xwrelay.h"
|
||||||
|
#include "devid.h"
|
||||||
|
#include "dbmgr.h"
|
||||||
#include "states.h"
|
#include "states.h"
|
||||||
|
|
||||||
typedef vector<unsigned char> MsgBuffer;
|
typedef vector<unsigned char> MsgBuffer;
|
||||||
|
@ -63,13 +65,6 @@ public:
|
||||||
class CookieRef* m_this;
|
class CookieRef* m_this;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DevID {
|
|
||||||
public:
|
|
||||||
DevID() { m_devIDType = 0; }
|
|
||||||
string m_devIDString;
|
|
||||||
unsigned char m_devIDType;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CookieRef {
|
class CookieRef {
|
||||||
public:
|
public:
|
||||||
set<int> GetSockets();
|
set<int> GetSockets();
|
||||||
|
@ -224,10 +219,12 @@ class CookieRef {
|
||||||
|
|
||||||
void handleEvents();
|
void handleEvents();
|
||||||
|
|
||||||
void sendResponse( const CRefEvent* evt, bool initial );
|
void sendResponse( const CRefEvent* evt, bool initial,
|
||||||
|
const DBMgr::DevIDRelay* devID );
|
||||||
void sendAnyStored( const CRefEvent* evt );
|
void sendAnyStored( const CRefEvent* evt );
|
||||||
void initPlayerCounts( const CRefEvent* evt );
|
void initPlayerCounts( const CRefEvent* evt );
|
||||||
bool increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp );
|
bool increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
|
||||||
|
DBMgr::DevIDRelay* devID );
|
||||||
void updateAck( HostID hostID, bool keep );
|
void updateAck( HostID hostID, bool keep );
|
||||||
void dropPending( int seed );
|
void dropPending( int seed );
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#define GAMES_TABLE "games"
|
#define GAMES_TABLE "games"
|
||||||
#define MSGS_TABLE "msgs"
|
#define MSGS_TABLE "msgs"
|
||||||
|
#define DEVICES_TABLE "devices"
|
||||||
|
|
||||||
#define ARRAYSUM "sum_array(nPerDevice)"
|
#define ARRAYSUM "sum_array(nPerDevice)"
|
||||||
|
|
||||||
|
@ -243,10 +244,59 @@ DBMgr::AllDevsAckd( const char* const connName )
|
||||||
return full;
|
return full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return DevIDRelay for device, adding it to devices table IFF it's not
|
||||||
|
// already there.
|
||||||
|
DBMgr::DevIDRelay
|
||||||
|
DBMgr::RegisterDevice( const DevID* host )
|
||||||
|
{
|
||||||
|
DBMgr::DevIDRelay devID;
|
||||||
|
assert( host->m_devIDType != ID_TYPE_RELAY );
|
||||||
|
int ii;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
// if it's already present, just return
|
||||||
|
devID = getDevID( host );
|
||||||
|
if ( DEVID_NONE == devID ) {
|
||||||
|
// loop until we're successful inserting the unique key. Ship with this
|
||||||
|
// coming from random, but test with increasing values initially to make
|
||||||
|
// sure duplicates are detected.
|
||||||
|
for ( success = false, ii = 0; !success; ++ii ) {
|
||||||
|
assert( 10 > ii ); // better to check that we're looping BECAUSE
|
||||||
|
// of uniqueness problem.
|
||||||
|
devID = (DBMgr::DevIDRelay)random();
|
||||||
|
if ( DEVID_NONE == devID ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char* command = "INSERT INTO " DEVICES_TABLE
|
||||||
|
" (id, devType, devid)"
|
||||||
|
" VALUES( $1, $2, $3 )";
|
||||||
|
int nParams = 3;
|
||||||
|
char* paramValues[nParams];
|
||||||
|
char buf[512];
|
||||||
|
formatParams( paramValues, nParams,
|
||||||
|
"%d"DELIM"%d"DELIM"%s",
|
||||||
|
buf, sizeof(buf), devID, host->m_devIDType,
|
||||||
|
host->m_devIDString.c_str() );
|
||||||
|
|
||||||
|
PGresult* result = PQexecParams( getThreadConn(), command,
|
||||||
|
nParams, NULL,
|
||||||
|
paramValues,
|
||||||
|
NULL, NULL, 0 );
|
||||||
|
success = PGRES_COMMAND_OK == PQresultStatus(result);
|
||||||
|
if ( !success ) {
|
||||||
|
logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)),
|
||||||
|
PQresultErrorMessage(result) );
|
||||||
|
}
|
||||||
|
PQclear( result );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return devID;
|
||||||
|
}
|
||||||
|
|
||||||
HostID
|
HostID
|
||||||
DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
||||||
int nToAdd, unsigned short seed, const in_addr& addr,
|
int nToAdd, unsigned short seed, const in_addr& addr,
|
||||||
const DevID* devID, bool ackd )
|
DevIDRelay devID, bool ackd )
|
||||||
{
|
{
|
||||||
HostID newID = curID;
|
HostID newID = curID;
|
||||||
|
|
||||||
|
@ -262,11 +312,9 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
||||||
assert( newID <= 4 );
|
assert( newID <= 4 );
|
||||||
|
|
||||||
char devIDBuf[512] = {0};
|
char devIDBuf[512] = {0};
|
||||||
if ( !!devID ) {
|
if ( DEVID_NONE != devID ) {
|
||||||
snprintf( devIDBuf, sizeof(devIDBuf),
|
snprintf( devIDBuf, sizeof(devIDBuf),
|
||||||
"devids[%d] = \'%s\', devTypes[%d] = %d,",
|
"devids[%d] = %d, ", newID, devID );
|
||||||
newID, devID->m_devIDString.c_str(),
|
|
||||||
newID, devID->m_devIDType );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = %d,"
|
const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = %d,"
|
||||||
|
@ -569,20 +617,45 @@ DBMgr::readArray( const char* const connName, int arr[] ) /* len 4 */
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
DBMgr::DevIDRelay
|
||||||
DBMgr::getDevID( const char* connName, int hid, DevID& devID )
|
DBMgr::getDevID( const char* connName, int hid )
|
||||||
{
|
{
|
||||||
const char* fmt = "SELECT devids[%d], devTypes[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
|
DBMgr::DevIDRelay devID;
|
||||||
|
const char* fmt = "SELECT devids[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
|
||||||
char query[256];
|
char query[256];
|
||||||
snprintf( query, sizeof(query), fmt, hid, hid, connName );
|
snprintf( query, sizeof(query), fmt, hid, connName );
|
||||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
|
||||||
|
|
||||||
PGresult* result = PQexec( getThreadConn(), query );
|
PGresult* result = PQexec( getThreadConn(), query );
|
||||||
assert( 1 == PQntuples( result ) );
|
assert( 1 == PQntuples( result ) );
|
||||||
devID.m_devIDString = PQgetvalue( result, 0, 0 );
|
devID = (DBMgr::DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
||||||
devID.m_devIDType = (unsigned char)atoi( PQgetvalue( result, 0, 1 ) );
|
|
||||||
assert( devID.m_devIDType <= 2 ); // for now!!!
|
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
|
return devID;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBMgr::DevIDRelay
|
||||||
|
DBMgr::getDevID( const DevID* devID )
|
||||||
|
{
|
||||||
|
DBMgr::DevIDRelay rDevID = DEVID_NONE;
|
||||||
|
DevIDType devIDType = devID->m_devIDType;
|
||||||
|
assert( ID_TYPE_NONE < devIDType );
|
||||||
|
const char* asStr = devID->m_devIDString.c_str();
|
||||||
|
if ( ID_TYPE_RELAY == devIDType ) {
|
||||||
|
rDevID = strtoul( asStr, NULL, 16 );
|
||||||
|
} else {
|
||||||
|
const char* fmt = "SELECT id FROM " DEVICES_TABLE " WHERE devtype=%d and devid = '%s'";
|
||||||
|
char query[512];
|
||||||
|
snprintf( query, sizeof(query), fmt, devIDType, asStr );
|
||||||
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
|
||||||
|
|
||||||
|
PGresult* result = PQexec( getThreadConn(), query );
|
||||||
|
assert( 1 >= PQntuples( result ) );
|
||||||
|
if ( 1 == PQntuples( result ) ) {
|
||||||
|
rDevID = (DBMgr::DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
||||||
|
}
|
||||||
|
PQclear( result );
|
||||||
|
}
|
||||||
|
return rDevID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -625,13 +698,12 @@ void
|
||||||
DBMgr::StoreMessage( const char* const connName, int hid,
|
DBMgr::StoreMessage( const char* const connName, int hid,
|
||||||
const unsigned char* buf, int len )
|
const unsigned char* buf, int len )
|
||||||
{
|
{
|
||||||
DevID devID;
|
DevIDRelay devID = getDevID( connName, hid );
|
||||||
getDevID( connName, hid, devID );
|
|
||||||
|
|
||||||
size_t newLen;
|
size_t newLen;
|
||||||
const char* fmt = "INSERT INTO " MSGS_TABLE
|
const char* fmt = "INSERT INTO " MSGS_TABLE
|
||||||
" (connname, hid, devid, devType, msg, msglen)"
|
" (connname, hid, devid, msg, msglen)"
|
||||||
" VALUES( '%s', %d, '%s', %d, E'%s', %d)";
|
" VALUES( '%s', %d, %d, E'%s', %d)";
|
||||||
|
|
||||||
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
|
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
|
||||||
len, &newLen );
|
len, &newLen );
|
||||||
|
@ -639,8 +711,7 @@ DBMgr::StoreMessage( const char* const connName, int hid,
|
||||||
|
|
||||||
char query[1024];
|
char query[1024];
|
||||||
size_t siz = snprintf( query, sizeof(query), fmt, connName, hid,
|
size_t siz = snprintf( query, sizeof(query), fmt, connName, hid,
|
||||||
devID.m_devIDString.c_str(),
|
devID, bytes, len );
|
||||||
devID.m_devIDType, bytes, len );
|
|
||||||
|
|
||||||
PQfreemem( bytes );
|
PQfreemem( bytes );
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,19 @@
|
||||||
|
|
||||||
#include "xwrelay.h"
|
#include "xwrelay.h"
|
||||||
#include "xwrelay_priv.h"
|
#include "xwrelay_priv.h"
|
||||||
#include "cref.h"
|
#include "devid.h"
|
||||||
#include <libpq-fe.h>
|
#include <libpq-fe.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class DBMgr {
|
class DBMgr {
|
||||||
public:
|
public:
|
||||||
|
/* DevIDs on various platforms are stored in devices table. This is the
|
||||||
|
key, and used in msgs and games tables as a shorter way to refer to
|
||||||
|
them. */
|
||||||
|
typedef unsigned int DevIDRelay;
|
||||||
|
static const DevIDRelay DEVID_NONE = 0;
|
||||||
|
|
||||||
static DBMgr* Get();
|
static DBMgr* Get();
|
||||||
|
|
||||||
~DBMgr();
|
~DBMgr();
|
||||||
|
@ -56,9 +62,11 @@ class DBMgr {
|
||||||
char* connNameBuf, int bufLen, int* nPlayersHP );
|
char* connNameBuf, int bufLen, int* nPlayersHP );
|
||||||
bool AllDevsAckd( const char* const connName );
|
bool AllDevsAckd( const char* const connName );
|
||||||
|
|
||||||
|
DevIDRelay RegisterDevice( const DevID* hosts );
|
||||||
|
|
||||||
HostID AddDevice( const char* const connName, HostID curID, int clientVersion,
|
HostID AddDevice( const char* const connName, HostID curID, int clientVersion,
|
||||||
int nToAdd, unsigned short seed, const in_addr& addr,
|
int nToAdd, unsigned short seed, const in_addr& addr,
|
||||||
const DevID* devID, bool unAckd );
|
DevIDRelay devID, bool unAckd );
|
||||||
void NoteAckd( const char* const connName, HostID id );
|
void NoteAckd( const char* const connName, HostID id );
|
||||||
HostID HIDForSeed( const char* const connName, unsigned short seed );
|
HostID HIDForSeed( const char* const connName, unsigned short seed );
|
||||||
bool RmDeviceByHid( const char* const connName, HostID id );
|
bool RmDeviceByHid( const char* const connName, HostID id );
|
||||||
|
@ -99,7 +107,8 @@ class DBMgr {
|
||||||
DBMgr();
|
DBMgr();
|
||||||
bool execSql( const char* const query ); /* no-results query */
|
bool execSql( const char* const query ); /* no-results query */
|
||||||
void readArray( const char* const connName, int arr[] );
|
void readArray( const char* const connName, int arr[] );
|
||||||
void getDevID( const char* connName, int hid, DevID& devID );
|
DevIDRelay getDevID( const char* connName, int hid );
|
||||||
|
DevIDRelay getDevID( const DevID* devID );
|
||||||
|
|
||||||
PGconn* getThreadConn( void );
|
PGconn* getThreadConn( void );
|
||||||
|
|
||||||
|
|
64
xwords4/relay/devid.h
Normal file
64
xwords4/relay/devid.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2005-2012 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.
|
||||||
|
*
|
||||||
|
* 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 _DEVID_H_
|
||||||
|
#define _DEVID_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "xwrelay.h"
|
||||||
|
|
||||||
|
/* DevID protocol.
|
||||||
|
*
|
||||||
|
* There are two types. The first, with a DevIDType greater than
|
||||||
|
* ID_TYPE_RELAY, is platform-specific and meaningless to the relay (though as
|
||||||
|
* with GCM-based IDs on Android there may be server code that uses it.) The
|
||||||
|
* second, with type of ID_TYPE_RELAY, is specific to the relay. When the
|
||||||
|
* relay sees one of the first type, it creates an entry in the devices table
|
||||||
|
* with a new random 32-bit index that is then used in the msgs and games
|
||||||
|
* tables. This index is the second type.
|
||||||
|
*
|
||||||
|
* A device always includes a DevID when creating a new game. It may be of
|
||||||
|
* either type, and generally should use the latter when possible. Since the
|
||||||
|
* latter comes from the relay, the first time a device connects (after
|
||||||
|
* whatever local event, e.g. registration with GCM, causes it to have an ID of
|
||||||
|
* the first type that it wants to share) it will have to send the first type.
|
||||||
|
* When replying to a registration message that included a DevID of the first
|
||||||
|
* type, the relay always sends the corresponding DevID of the second type,
|
||||||
|
* which it expects the device to remember. But when replying after receiving a
|
||||||
|
* DevID of the second type the relay does not echo that value (sends an empty
|
||||||
|
* string).
|
||||||
|
*
|
||||||
|
* Devices or platforms not providing a DevID will return ID_TYPE_NONE as the
|
||||||
|
* type via util_getDevID(). That single byte will be transmitted to the relay
|
||||||
|
* which will then skip the registration process and will return an empty
|
||||||
|
* string as the relay-type DevID in the connection response.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class DevID {
|
||||||
|
public:
|
||||||
|
DevID() { m_devIDType = ID_TYPE_NONE; }
|
||||||
|
string m_devIDString;
|
||||||
|
DevIDType m_devIDType;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -26,12 +26,15 @@ QUERY="WHERE NOT -NTOTAL = sum_array(nperdevice)"
|
||||||
echo "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')"
|
echo "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')"
|
||||||
echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;")
|
echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;")
|
||||||
|
|
||||||
echo "SELECT dead,connname,cid,room,lang,clntVers,ntotal,nperdevice,seeds,devTypes,devids,ack,nsent "\
|
echo "SELECT dead,connname,cid,room,lang,clntVers,ntotal,nperdevice,seeds,devids,ack,nsent "\
|
||||||
"FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \
|
"FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \
|
||||||
| psql xwgames
|
| psql xwgames
|
||||||
|
|
||||||
echo "SELECT connname, hid, devType, devid, count(*), sum(msglen) "\
|
echo "SELECT connname, hid, devid, count(*), sum(msglen) "\
|
||||||
"FROM msgs where connname in (SELECT connname from games where not games.dead group by connname)" \
|
"FROM msgs where connname in (SELECT connname from games where not games.dead group by connname)" \
|
||||||
"GROUP BY connname, hid, devType, devid ORDER BY connname;" \
|
"GROUP BY connname, hid, devid ORDER BY connname;" \
|
||||||
|
| psql xwgames
|
||||||
|
|
||||||
|
echo "SELECT * from devices;" \
|
||||||
| psql xwgames
|
| psql xwgames
|
||||||
|
|
||||||
|
|
|
@ -386,7 +386,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr )
|
||||||
if ( getNetByte( &bufp, end, &devIDType )
|
if ( getNetByte( &bufp, end, &devIDType )
|
||||||
&& 0 != devIDType ) {
|
&& 0 != devIDType ) {
|
||||||
getNetString( &bufp, end, devID.m_devIDString );
|
getNetString( &bufp, end, devID.m_devIDString );
|
||||||
devID.m_devIDType = devIDType;
|
devID.m_devIDType = (DevIDType)devIDType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,13 @@ enum { XWRELAY_NONE /* 0 is an illegal value */
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ID_TYPE_NONE
|
||||||
|
,ID_TYPE_RELAY /* assigned by relay as replacement for one of the below */
|
||||||
|
,ID_TYPE_LINUX
|
||||||
|
,ID_TYPE_ANDROID_GCM
|
||||||
|
} DevIDType;
|
||||||
|
|
||||||
#ifndef CANT_DO_TYPEDEF
|
#ifndef CANT_DO_TYPEDEF
|
||||||
typedef unsigned char XWRELAY_Cmd;
|
typedef unsigned char XWRELAY_Cmd;
|
||||||
#endif
|
#endif
|
||||||
|
@ -115,6 +122,7 @@ typedef unsigned char XWRELAY_Cmd;
|
||||||
#define MAX_INVITE_LEN 31
|
#define MAX_INVITE_LEN 31
|
||||||
#define MAX_MSG_LEN 1024 /* Used for proxy too! */
|
#define MAX_MSG_LEN 1024 /* Used for proxy too! */
|
||||||
#define MAX_CONNNAME_LEN 48 /* host ID, boot time, and seeds as hex? */
|
#define MAX_CONNNAME_LEN 48 /* host ID, boot time, and seeds as hex? */
|
||||||
|
#define MAX_DEVID_LEN 8 /* 32-bit number as hex */
|
||||||
|
|
||||||
#define XWRELAY_PROTO_VERSION_ORIG 0x01
|
#define XWRELAY_PROTO_VERSION_ORIG 0x01
|
||||||
#define XWRELAY_PROTO_VERSION_LATE_NAME 0x02
|
#define XWRELAY_PROTO_VERSION_LATE_NAME 0x02
|
||||||
|
|
|
@ -58,8 +58,7 @@ cid integer
|
||||||
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
|
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
|
||||||
,mtimes TIMESTAMP(0)[]
|
,mtimes TIMESTAMP(0)[]
|
||||||
,addrs INET[]
|
,addrs INET[]
|
||||||
,devTypes INTEGER[]
|
,devids INTEGER[]
|
||||||
,devids TEXT[]
|
|
||||||
);
|
);
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -69,12 +68,20 @@ id SERIAL
|
||||||
,connName VARCHAR(64)
|
,connName VARCHAR(64)
|
||||||
,hid INTEGER
|
,hid INTEGER
|
||||||
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
,devType INTEGER
|
,devid INTEGER
|
||||||
,devid TEXT
|
|
||||||
,msg BYTEA
|
,msg BYTEA
|
||||||
,msglen INTEGER
|
,msglen INTEGER
|
||||||
,UNIQUE ( connName, hid, msg )
|
,UNIQUE ( connName, hid, msg )
|
||||||
);
|
);
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat | psql $DBNAME --file - <<EOF
|
||||||
|
CREATE TABLE devices (
|
||||||
|
id INTEGER UNIQUE PRIMARY KEY
|
||||||
|
,devType INTEGER
|
||||||
|
,devid TEXT
|
||||||
|
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue