mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-28 07:58:08 +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. -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.eehouse.android.xw4"
|
||||
android:versionCode="45"
|
||||
android:versionCode="46"
|
||||
android:versionName="@string/app_version"
|
||||
>
|
||||
|
||||
|
|
|
@ -5,36 +5,17 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<b>Crosswords 4.4 beta 53 release</b>
|
||||
<b>Crosswords 4.4 beta 54 release</b>
|
||||
<ul>
|
||||
|
||||
<li>Add a second way devices can play: via SMS. If you and your
|
||||
friends have unlimited texting plans (ONLY IF!!) this is the way to
|
||||
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>Don't try to access directory OS says is for downloads when it
|
||||
doesn't actually exist</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>
|
||||
|
||||
<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
|
||||
eehouse@eehouse.org) what's broken and what features you'd most like
|
||||
to see.</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_version">4.4 beta 53</string>
|
||||
<string name="app_version">4.4 beta 54</string>
|
||||
</resources>
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
<string name="key_na_browse">key_na_browse</string>
|
||||
<string name="key_na_values">key_na_values</string>
|
||||
<string name="key_enable_debug">key_enable_debug</string>
|
||||
<string name="key_download_path">key_download_path</string>
|
||||
|
||||
<!-- Nor is my email address -->
|
||||
<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_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>
|
||||
|
|
|
@ -299,6 +299,10 @@
|
|||
android:summary="Menuitems etc."
|
||||
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"
|
||||
android:title="Show SMS sends, receives"
|
||||
android:defaultValue="false"
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Comparator;
|
|||
import junit.framework.Assert;
|
||||
|
||||
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.XwJNI;
|
||||
import org.eehouse.android.xw4.jni.DictInfo;
|
||||
|
@ -244,7 +245,7 @@ public class DictLangCache {
|
|||
|
||||
// May be called from background thread
|
||||
public static void inval( final Context context, String name,
|
||||
DictUtils.DictLoc loc, boolean added )
|
||||
DictLoc loc, boolean added )
|
||||
{
|
||||
DictAndLoc dal = new DictAndLoc( name, loc );
|
||||
DBUtils.dictsRemoveInfo( context, dal );
|
||||
|
@ -372,7 +373,7 @@ public class DictLangCache {
|
|||
{
|
||||
DictInfo result = DBUtils.dictsGetInfo( context, name );
|
||||
if ( null == result ) {
|
||||
DictUtils.DictLoc loc = DictUtils.getDictLoc( context, name );
|
||||
DictLoc loc = DictUtils.getDictLoc( context, name );
|
||||
result = getInfo( context, new DictAndLoc( name, loc ) );
|
||||
}
|
||||
return result;
|
||||
|
@ -397,7 +398,7 @@ public class DictLangCache {
|
|||
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
|
||||
pairs.m_paths[0],
|
||||
JNIUtilsImpl.get( context ),
|
||||
DictUtils.DictLoc.DOWNLOAD == dal.loc,
|
||||
DictLoc.DOWNLOAD == dal.loc,
|
||||
info ) ) {
|
||||
|
||||
info.name = dal.name;
|
||||
|
|
|
@ -149,7 +149,8 @@ public class DictUtils {
|
|||
}
|
||||
|
||||
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 =
|
||||
al.toArray( new DictUtils.DictAndLoc[al.size()] );
|
||||
|
@ -248,7 +249,7 @@ public class DictUtils {
|
|||
File path = null;
|
||||
switch( loc ) {
|
||||
case DOWNLOAD:
|
||||
path = getDownloadsPathFor( name );
|
||||
path = getDownloadsPathFor( context, name );
|
||||
break;
|
||||
case EXTERNAL:
|
||||
path = getSDPathFor( context, name );
|
||||
|
@ -305,7 +306,7 @@ public class DictUtils {
|
|||
FileInputStream fis = null;
|
||||
if ( null == fis ) {
|
||||
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.DOWNLOAD ) {
|
||||
File path = getDownloadsPathFor( name );
|
||||
File path = getDownloadsPathFor( context, name );
|
||||
if ( null != path && path.exists() ) {
|
||||
DbgUtils.logf( "loading %s from Download", name );
|
||||
fis = new FileInputStream( path );
|
||||
|
@ -365,7 +366,7 @@ public class DictUtils {
|
|||
File path;
|
||||
switch ( to ) {
|
||||
case DOWNLOAD:
|
||||
path = getDownloadsPathFor( name );
|
||||
path = getDownloadsPathFor( context, name );
|
||||
break;
|
||||
case EXTERNAL:
|
||||
path = getSDPathFor( context, name );
|
||||
|
@ -560,25 +561,36 @@ public class DictUtils {
|
|||
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;
|
||||
if ( haveWriteableSD() ) {
|
||||
File storage = Environment.getExternalStorageDirectory();
|
||||
if ( null != storage ) {
|
||||
result = new File( storage.getPath(), "download/" );
|
||||
if ( !result.exists() ) {
|
||||
result = null;
|
||||
File file = null;
|
||||
String myPath = XWPrefs.getMyDownloadDir( context );
|
||||
if ( null != myPath && 0 < myPath.length() ) {
|
||||
file = new File( myPath );
|
||||
} else {
|
||||
file = Environment.getExternalStorageDirectory();
|
||||
if ( null != file ) {
|
||||
file = new File( file, "download/" );
|
||||
}
|
||||
}
|
||||
if ( null != file && file.exists() && file.isDirectory() ) {
|
||||
result = file;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static File getDownloadsPathFor( String name )
|
||||
private static File getDownloadsPathFor( Context context, String name )
|
||||
{
|
||||
File result = null;
|
||||
File dir = getDownloadDir();
|
||||
File dir = getDownloadDir( context );
|
||||
if ( dir != null ) {
|
||||
result = new File( dir, name );
|
||||
}
|
||||
|
|
|
@ -263,15 +263,6 @@ public class DictsActivity extends ExpandableListActivity
|
|||
message = Utils.format( this, R.string.move_dictf,
|
||||
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 =
|
||||
new OnClickListener() {
|
||||
public void onClick( DialogInterface dlgi, int item ) {
|
||||
|
@ -307,7 +298,8 @@ public class DictsActivity extends ExpandableListActivity
|
|||
|
||||
dialog = new AlertDialog.Builder( this )
|
||||
.setTitle( message )
|
||||
.setSingleChoiceItems( items, m_moveFromItem, newSelLstnr )
|
||||
.setSingleChoiceItems( makeDictDirItems(), m_moveFromItem,
|
||||
newSelLstnr )
|
||||
.setPositiveButton( R.string.button_move, lstnr )
|
||||
.setNegativeButton( R.string.button_cancel, null )
|
||||
.create();
|
||||
|
@ -716,6 +708,26 @@ public class DictsActivity extends ExpandableListActivity
|
|||
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 )
|
||||
{
|
||||
Uri uri = Uri.parse( dict_url );
|
||||
|
|
|
@ -208,12 +208,20 @@ public class DlgDelegate {
|
|||
|
||||
public void showConfirmThen( String msg, int posButton, int callbackID )
|
||||
{
|
||||
m_msg = msg;
|
||||
m_posButton = posButton;
|
||||
Assert.assertTrue( 0 != callbackID );
|
||||
Assert.assertTrue( 0 == m_cbckID );
|
||||
m_cbckID = callbackID;
|
||||
m_activity.showDialog( CONFIRM_THEN );
|
||||
// FIX ME!! Need to store data per message rather than have
|
||||
// assertions failing or messages dropped.
|
||||
|
||||
if ( 0 != m_cbckID ) {
|
||||
DbgUtils.logf( "showConfirmThen: busy with another message; "
|
||||
+ "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 )
|
||||
|
|
|
@ -730,7 +730,6 @@ public class GameUtils {
|
|||
}
|
||||
lock.unlock();
|
||||
}
|
||||
DbgUtils.logf( "feedMessages=>%b", draw );
|
||||
return draw;
|
||||
} // feedMessages
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import java.io.File;
|
||||
|
||||
public class PrefsActivity extends PreferenceActivity
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
@ -40,6 +41,7 @@ public class PrefsActivity extends PreferenceActivity
|
|||
private String m_keyLogging;
|
||||
private String m_smsToasting;
|
||||
private String m_smsEnable;
|
||||
private String m_downloadPath;
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog( int id )
|
||||
|
@ -120,6 +122,7 @@ public class PrefsActivity extends PreferenceActivity
|
|||
m_keyLogging = getString( R.string.key_logging_on );
|
||||
m_smsToasting = getString( R.string.key_show_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.setOnClickListener( new View.OnClickListener() {
|
||||
|
@ -164,6 +167,23 @@ public class PrefsActivity extends PreferenceActivity
|
|||
} else {
|
||||
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 ) {
|
||||
case CHECK_MSGDB:
|
||||
if ( ! XWPrefs.getHaveCheckedSMS( this ) ) {
|
||||
Utils.showToast( this, R.string.sms_searching_toast );
|
||||
XWPrefs.setHaveCheckedSMS( this, true );
|
||||
new Thread( new Runnable() {
|
||||
public void run() {
|
||||
|
|
|
@ -216,6 +216,11 @@ public class XWPrefs {
|
|||
: DictUtils.DictLoc.EXTERNAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getMyDownloadDir( Context context )
|
||||
{
|
||||
return getPrefsString( context, R.string.key_download_path );
|
||||
}
|
||||
|
||||
public static boolean getDefaultLocInternal( Context context )
|
||||
{
|
||||
|
|
|
@ -1330,6 +1330,16 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
|
|||
sizeof(comms->r.connName) );
|
||||
#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->addr.u.ip_relay.invite, reconnected,
|
||||
comms->r.myHostID, XP_FALSE, nSought - nHere );
|
||||
|
|
|
@ -55,12 +55,6 @@ typedef enum {
|
|||
, COMMS_RELAYSTATE_ALLCONNECTED
|
||||
} CommsRelayState;
|
||||
|
||||
typedef enum {
|
||||
ID_TYPE_NONE
|
||||
,ID_TYPE_LINUX
|
||||
,ID_TYPE_ANDROID_GCM
|
||||
} DevIDType;
|
||||
|
||||
#ifdef XWFEATURE_BLUETOOTH
|
||||
# define XW_BT_UUID "7be0d084-ff89-4d6d-9c78-594773a6f963"
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ typedef struct UtilVtable {
|
|||
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, const XP_UCHAR* idRelay );
|
||||
#endif
|
||||
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
|
||||
|
||||
|
@ -285,6 +286,8 @@ struct XW_UtilCtxt {
|
|||
#ifdef XWFEATURE_DEVID
|
||||
# define util_getDevID( uc, t ) \
|
||||
(uc)->vtable->m_util_getDevID((uc),(t))
|
||||
# define util_deviceRegistered( uc, id ) \
|
||||
(uc)->vtable->m_util_deviceRegistered( (uc), (id) )
|
||||
#endif
|
||||
|
||||
#define util_makeEmptyDict( uc ) \
|
||||
|
|
|
@ -505,6 +505,7 @@ typedef enum {
|
|||
,CMD_SEED
|
||||
#ifdef XWFEATURE_DEVID
|
||||
,CMD_DEVID
|
||||
,CMD_RDEVID
|
||||
#endif
|
||||
,CMD_GAMESEED
|
||||
,CMD_GAMEFILE
|
||||
|
@ -601,6 +602,7 @@ static CmdInfoRec CmdInfoRecs[] = {
|
|||
,{ CMD_SEED, true, "seed", "random seed" }
|
||||
#ifdef XWFEATURE_DEVID
|
||||
,{ CMD_DEVID, true, "devid", "device ID (for testing GCM stuff)" }
|
||||
,{ CMD_RDEVID, true, "rdevid", "relay's converted device ID (for testing GCM stuff)" }
|
||||
#endif
|
||||
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
|
||||
,{ CMD_GAMEFILE, true, "file", "file to save to/read from" }
|
||||
|
@ -1683,6 +1685,9 @@ main( int argc, char** argv )
|
|||
case CMD_DEVID:
|
||||
mainParams.devID = optarg;
|
||||
break;
|
||||
case CMD_RDEVID:
|
||||
mainParams.rDevID = optarg;
|
||||
break;
|
||||
#endif
|
||||
case CMD_GAMESEED:
|
||||
mainParams.gameSeed = atoi(optarg);
|
||||
|
|
|
@ -348,9 +348,25 @@ linux_util_getUserString( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 code )
|
|||
static const XP_UCHAR*
|
||||
linux_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
|
||||
{
|
||||
XP_UCHAR* result;
|
||||
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
|
||||
*typ = ID_TYPE_LINUX;
|
||||
return cGlobals->params->devID;
|
||||
if ( !!cGlobals->params->rDevID ) {
|
||||
*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
|
||||
|
||||
|
@ -365,6 +381,7 @@ linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
|
|||
util->vtable->m_util_getUserString = linux_util_getUserString;
|
||||
#ifdef XWFEATURE_DEVID
|
||||
util->vtable->m_util_getDevID = linux_util_getDevID;
|
||||
util->vtable->m_util_deviceRegistered = linux_util_deviceRegistered;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ typedef struct LaunchParams {
|
|||
char* bonusFile;
|
||||
#ifdef XWFEATURE_DEVID
|
||||
char* devID;
|
||||
char* rDevID;
|
||||
#endif
|
||||
VTableMgr* vtMgr;
|
||||
XP_U16 nLocalPlayers;
|
||||
|
|
|
@ -190,6 +190,7 @@ build_cmds() {
|
|||
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
|
||||
# PARAMS="$PARAMS --savefail-pct 10"
|
||||
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
|
||||
# PARAMS="$PARAMS --devid LINUX_TEST_$(printf %.5d ${COUNTER})"
|
||||
PARAMS="$PARAMS $PUBLIC"
|
||||
ARGS[$COUNTER]=$PARAMS
|
||||
ROOMS[$COUNTER]=$ROOM
|
||||
|
@ -380,6 +381,19 @@ increment_drop() {
|
|||
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() {
|
||||
ENDTIME=$(($(date +%s) + TIMEOUT))
|
||||
while :; do
|
||||
|
@ -409,6 +423,7 @@ run_cmds() {
|
|||
PIDS[$KEY]=0
|
||||
ROOM_PIDS[$ROOM]=0
|
||||
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
|
||||
# set_relay_devid $KEY
|
||||
check_game $KEY
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
@ -641,9 +642,10 @@ CookieRef::handleEvents()
|
|||
case XWA_SEND_CONNRSP:
|
||||
{
|
||||
HostID hid;
|
||||
if ( increasePlayerCounts( &evt, false, &hid ) ) {
|
||||
DBMgr::DevIDRelay devID;
|
||||
if ( increasePlayerCounts( &evt, false, &hid, &devID ) ) {
|
||||
setAllConnectedTimer();
|
||||
sendResponse( &evt, true );
|
||||
sendResponse( &evt, true, &devID );
|
||||
setAckTimer( hid );
|
||||
}
|
||||
}
|
||||
|
@ -666,8 +668,8 @@ CookieRef::handleEvents()
|
|||
/* break; */
|
||||
|
||||
case XWA_SEND_RERSP:
|
||||
increasePlayerCounts( &evt, true, NULL );
|
||||
sendResponse( &evt, false );
|
||||
increasePlayerCounts( &evt, true, NULL, NULL );
|
||||
sendResponse( &evt, false, NULL );
|
||||
sendAnyStored( &evt );
|
||||
postCheckAllHere();
|
||||
break;
|
||||
|
@ -855,8 +857,10 @@ CookieRef::send_stored_messages( HostID dest, int socket )
|
|||
} /* send_stored_messages */
|
||||
|
||||
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 socket = evt->u.con.socket;
|
||||
int seed = evt->u.con.seed;
|
||||
|
@ -883,10 +887,25 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
|
|||
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 =
|
||||
DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
|
||||
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;
|
||||
if ( NULL != hidp ) {
|
||||
|
@ -1016,7 +1035,8 @@ CookieRef::cancelAllConnectedTimer()
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1028,6 +1048,7 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
|||
+ sizeof(unsigned char) /* total here */
|
||||
+ sizeof(unsigned char) /* total expected */
|
||||
+ 1 + MAX_CONNNAME_LEN
|
||||
+ 1 + 1 + MAX_DEVID_LEN
|
||||
];
|
||||
|
||||
unsigned char* bufp = buf;
|
||||
|
@ -1043,12 +1064,33 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
|
|||
|
||||
const char* connName = ConnName();
|
||||
assert( !!connName && connName[0] );
|
||||
int len = strlen( connName );
|
||||
size_t len = strlen( connName );
|
||||
assert( len < MAX_CONNNAME_LEN );
|
||||
*bufp++ = (char)len;
|
||||
memcpy( bufp, connName, 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 );
|
||||
logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) );
|
||||
} /* sendResponse */
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <pthread.h>
|
||||
#include "xwrelay_priv.h"
|
||||
#include "xwrelay.h"
|
||||
#include "devid.h"
|
||||
#include "dbmgr.h"
|
||||
#include "states.h"
|
||||
|
||||
typedef vector<unsigned char> MsgBuffer;
|
||||
|
@ -63,13 +65,6 @@ public:
|
|||
class CookieRef* m_this;
|
||||
};
|
||||
|
||||
class DevID {
|
||||
public:
|
||||
DevID() { m_devIDType = 0; }
|
||||
string m_devIDString;
|
||||
unsigned char m_devIDType;
|
||||
};
|
||||
|
||||
class CookieRef {
|
||||
public:
|
||||
set<int> GetSockets();
|
||||
|
@ -224,10 +219,12 @@ class CookieRef {
|
|||
|
||||
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 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 dropPending( int seed );
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#define GAMES_TABLE "games"
|
||||
#define MSGS_TABLE "msgs"
|
||||
#define DEVICES_TABLE "devices"
|
||||
|
||||
#define ARRAYSUM "sum_array(nPerDevice)"
|
||||
|
||||
|
@ -243,10 +244,59 @@ DBMgr::AllDevsAckd( const char* const connName )
|
|||
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
|
||||
DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
||||
int nToAdd, unsigned short seed, const in_addr& addr,
|
||||
const DevID* devID, bool ackd )
|
||||
DevIDRelay devID, bool ackd )
|
||||
{
|
||||
HostID newID = curID;
|
||||
|
||||
|
@ -262,11 +312,9 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
|||
assert( newID <= 4 );
|
||||
|
||||
char devIDBuf[512] = {0};
|
||||
if ( !!devID ) {
|
||||
if ( DEVID_NONE != devID ) {
|
||||
snprintf( devIDBuf, sizeof(devIDBuf),
|
||||
"devids[%d] = \'%s\', devTypes[%d] = %d,",
|
||||
newID, devID->m_devIDString.c_str(),
|
||||
newID, devID->m_devIDType );
|
||||
"devids[%d] = %d, ", newID, devID );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
void
|
||||
DBMgr::getDevID( const char* connName, int hid, DevID& devID )
|
||||
DBMgr::DevIDRelay
|
||||
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];
|
||||
snprintf( query, sizeof(query), fmt, hid, hid, connName );
|
||||
snprintf( query, sizeof(query), fmt, hid, connName );
|
||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
|
||||
|
||||
PGresult* result = PQexec( getThreadConn(), query );
|
||||
assert( 1 == PQntuples( result ) );
|
||||
devID.m_devIDString = PQgetvalue( result, 0, 0 );
|
||||
devID.m_devIDType = (unsigned char)atoi( PQgetvalue( result, 0, 1 ) );
|
||||
assert( devID.m_devIDType <= 2 ); // for now!!!
|
||||
devID = (DBMgr::DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
||||
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,
|
||||
const unsigned char* buf, int len )
|
||||
{
|
||||
DevID devID;
|
||||
getDevID( connName, hid, devID );
|
||||
DevIDRelay devID = getDevID( connName, hid );
|
||||
|
||||
size_t newLen;
|
||||
const char* fmt = "INSERT INTO " MSGS_TABLE
|
||||
" (connname, hid, devid, devType, msg, msglen)"
|
||||
" VALUES( '%s', %d, '%s', %d, E'%s', %d)";
|
||||
" (connname, hid, devid, msg, msglen)"
|
||||
" VALUES( '%s', %d, %d, E'%s', %d)";
|
||||
|
||||
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
|
||||
len, &newLen );
|
||||
|
@ -639,8 +711,7 @@ DBMgr::StoreMessage( const char* const connName, int hid,
|
|||
|
||||
char query[1024];
|
||||
size_t siz = snprintf( query, sizeof(query), fmt, connName, hid,
|
||||
devID.m_devIDString.c_str(),
|
||||
devID.m_devIDType, bytes, len );
|
||||
devID, bytes, len );
|
||||
|
||||
PQfreemem( bytes );
|
||||
|
||||
|
|
|
@ -26,13 +26,19 @@
|
|||
|
||||
#include "xwrelay.h"
|
||||
#include "xwrelay_priv.h"
|
||||
#include "cref.h"
|
||||
#include "devid.h"
|
||||
#include <libpq-fe.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DBMgr {
|
||||
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();
|
||||
|
||||
~DBMgr();
|
||||
|
@ -56,9 +62,11 @@ class DBMgr {
|
|||
char* connNameBuf, int bufLen, int* nPlayersHP );
|
||||
bool AllDevsAckd( const char* const connName );
|
||||
|
||||
DevIDRelay RegisterDevice( const DevID* hosts );
|
||||
|
||||
HostID AddDevice( const char* const connName, HostID curID, int clientVersion,
|
||||
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 );
|
||||
HostID HIDForSeed( const char* const connName, unsigned short seed );
|
||||
bool RmDeviceByHid( const char* const connName, HostID id );
|
||||
|
@ -99,7 +107,8 @@ class DBMgr {
|
|||
DBMgr();
|
||||
bool execSql( const char* const query ); /* no-results query */
|
||||
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 );
|
||||
|
||||
|
|
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 "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;" \
|
||||
| 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)" \
|
||||
"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
|
||||
|
||||
|
|
|
@ -386,7 +386,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr )
|
|||
if ( getNetByte( &bufp, end, &devIDType )
|
||||
&& 0 != devIDType ) {
|
||||
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
|
||||
;
|
||||
|
||||
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
|
||||
typedef unsigned char XWRELAY_Cmd;
|
||||
#endif
|
||||
|
@ -115,6 +122,7 @@ typedef unsigned char XWRELAY_Cmd;
|
|||
#define MAX_INVITE_LEN 31
|
||||
#define MAX_MSG_LEN 1024 /* Used for proxy too! */
|
||||
#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_LATE_NAME 0x02
|
||||
|
|
|
@ -58,8 +58,7 @@ cid integer
|
|||
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
|
||||
,mtimes TIMESTAMP(0)[]
|
||||
,addrs INET[]
|
||||
,devTypes INTEGER[]
|
||||
,devids TEXT[]
|
||||
,devids INTEGER[]
|
||||
);
|
||||
EOF
|
||||
|
||||
|
@ -69,12 +68,20 @@ id SERIAL
|
|||
,connName VARCHAR(64)
|
||||
,hid INTEGER
|
||||
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
,devType INTEGER
|
||||
,devid TEXT
|
||||
,devid INTEGER
|
||||
,msg BYTEA
|
||||
,msglen INTEGER
|
||||
,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
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue