Merge branch 'gtk_multigame' into android_branch

This commit is contained in:
Eric House 2013-07-09 19:35:26 -07:00
commit 5462a05692
71 changed files with 5811 additions and 2995 deletions

View file

@ -995,8 +995,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_comms_1start
( JNIEnv* env, jclass C, jint gamePtr ) ( JNIEnv* env, jclass C, jint gamePtr )
{ {
XWJNI_START(); XWJNI_START();
if ( !!state->game.comms ) { CommsCtxt* comms = state->game.comms;
comms_start( state->game.comms ); if ( !!comms ) {
comms_start( comms );
}
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1stop
( JNIEnv* env, jclass C, jint gamePtr )
{
XWJNI_START();
CommsCtxt* comms = state->game.comms;
if ( !!comms ) {
comms_stop( comms );
} }
XWJNI_END(); XWJNI_END();
} }

View file

@ -38,6 +38,8 @@
<string name="key_sms_port">key_sms_port</string> <string name="key_sms_port">key_sms_port</string>
<string name="key_dict_host">key_dict_host3</string> <string name="key_dict_host">key_dict_host3</string>
<string name="key_logging_on">key_logging_on</string> <string name="key_logging_on">key_logging_on</string>
<string name="key_udp_relay">key_udp_relay</string>
<string name="key_drop_gcm">key_drop_gcm</string>
<string name="key_show_sms">key_show_sms</string> <string name="key_show_sms">key_show_sms</string>
<string name="key_init_hintsallowed">key_init_hintsallowed</string> <string name="key_init_hintsallowed">key_init_hintsallowed</string>
<string name="key_init_nethintsallowed">key_init_nethintsallowed</string> <string name="key_init_nethintsallowed">key_init_nethintsallowed</string>
@ -127,6 +129,9 @@
<string name="name_dict_fmt">%1$s/%2$s</string> <string name="name_dict_fmt">%1$s/%2$s</string>
<string name="gamel_menu_storedb">Write DB to SD card</string> <string name="gamel_menu_storedb">Write DB to SD card</string>
<string name="gamel_menu_loaddb">Load DB from SD card</string> <string name="gamel_menu_loaddb">Load DB from SD card</string>
<string name="udp_relay">New/Experimental relay connection</string>
<string name="drop_gcm_title">Ignore GCM notices</string>
<string name="drop_gcm_summary">(for testing relay proxy)</string>
<!--string name="dict_url">http://10.0.2.2/~eehouse/and_dicts</string--> <!--string name="dict_url">http://10.0.2.2/~eehouse/and_dicts</string-->

View file

@ -295,6 +295,15 @@
android:summary="@string/git_rev" android:summary="@string/git_rev"
android:enabled="false" android:enabled="false"
/> />
<CheckBoxPreference android:key="@string/key_udp_relay"
android:title="@string/udp_relay"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_drop_gcm"
android:title="@string/drop_gcm_title"
android:summary="@string/drop_gcm_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_logging_on" <CheckBoxPreference android:key="@string/key_logging_on"
android:title="@string/logging_on" android:title="@string/logging_on"
android:defaultValue="false" android:defaultValue="false"

View file

@ -29,7 +29,6 @@ import android.bluetooth.BluetoothSocket;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -49,7 +48,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent; import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
public class BTService extends Service { public class BTService extends XWService {
private static final long RESEND_TIMEOUT = 5; // seconds private static final long RESEND_TIMEOUT = 5; // seconds
private static final int MAX_SEND_FAIL = 3; private static final int MAX_SEND_FAIL = 3;
@ -77,8 +76,6 @@ public class BTService extends Service {
private static final String NTO_STR = "TOT"; private static final String NTO_STR = "TOT";
private static final String NHE_STR = "HER"; private static final String NHE_STR = "HER";
private static MultiService s_srcMgr = null;
private enum BTCmd { private enum BTCmd {
PING, PING,
PONG, PONG,
@ -147,16 +144,6 @@ public class BTService extends Service {
} }
} }
public static void setListener( MultiService.MultiEventListener li )
{
if ( XWApp.BTSUPPORTED ) {
if ( null == s_srcMgr ) {
s_srcMgr = new MultiService();
}
s_srcMgr.setListener( li );
}
}
public static void radioChanged( Context context, boolean cameOn ) public static void radioChanged( Context context, boolean cameOn )
{ {
Intent intent = getIntentTo( context, RADIO ); Intent intent = getIntentTo( context, RADIO );
@ -316,12 +303,6 @@ public class BTService extends Service {
return result; return result;
} }
@Override
public IBinder onBind( Intent intent )
{
return null;
}
private class BTListenerThread extends Thread { private class BTListenerThread extends Thread {
private BluetoothServerSocket m_serverSocket; private BluetoothServerSocket m_serverSocket;
@ -852,11 +833,6 @@ public class BTService extends Service {
sendResult( MultiEvent.SCAN_DONE, (Object)(names()) ); sendResult( MultiEvent.SCAN_DONE, (Object)(names()) );
} }
private void sendResult( MultiEvent event, Object ... args )
{
s_srcMgr.sendResult( event, args );
}
private void listLocalBTGames( boolean force ) private void listLocalBTGames( boolean force )
{ {
if ( null == s_devGames ) { if ( null == s_devGames ) {

View file

@ -190,6 +190,23 @@ public class BoardActivity extends XWActivity
return delivered; return delivered;
} }
public static boolean feedMessage( long rowid, byte[] msg )
{
boolean delivered = false;
synchronized( s_thisLocker ) {
if ( null != s_this ) {
Assert.assertNotNull( s_this.m_gi );
Assert.assertNotNull( s_this.m_gameLock );
Assert.assertNotNull( s_this.m_jniThread );
if ( rowid == s_this.m_rowid ) {
s_this.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, null );
delivered = true;
}
}
}
return delivered;
}
public static boolean feedMessages( long rowid, byte[][] msgs ) public static boolean feedMessages( long rowid, byte[][] msgs )
{ {
boolean delivered = false; boolean delivered = false;
@ -1693,7 +1710,7 @@ public class BoardActivity extends XWActivity
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_xport = new CommsTransport( m_jniGamePtr, this, this, m_xport = new CommsTransport( m_jniGamePtr, this, this,
m_gi.serverRole ); m_rowid, m_gi.serverRole );
} }
CommonPrefs cp = CommonPrefs.get( this ); CommonPrefs cp = CommonPrefs.get( this );

View file

@ -57,6 +57,7 @@ public class CommsTransport implements TransportProcs,
private ByteBuffer m_bytesIn; private ByteBuffer m_bytesIn;
private Context m_context; private Context m_context;
private long m_rowid;
// assembling inbound packet // assembling inbound packet
private byte[] m_packetIn; private byte[] m_packetIn;
@ -64,11 +65,12 @@ public class CommsTransport implements TransportProcs,
public CommsTransport( int jniGamePtr, Context context, public CommsTransport( int jniGamePtr, Context context,
TransportProcs.TPMsgHandler handler, TransportProcs.TPMsgHandler handler,
DeviceRole role ) long rowid, DeviceRole role )
{ {
m_jniGamePtr = jniGamePtr; m_jniGamePtr = jniGamePtr;
m_context = context; m_context = context;
m_tpHandler = handler; m_tpHandler = handler;
m_rowid = rowid;
m_buffersOut = new Vector<ByteBuffer>(); m_buffersOut = new Vector<ByteBuffer>();
m_bytesIn = ByteBuffer.allocate( 2048 ); m_bytesIn = ByteBuffer.allocate( 2048 );
@ -377,13 +379,17 @@ public class CommsTransport implements TransportProcs,
switch ( addr.conType ) { switch ( addr.conType ) {
case COMMS_CONN_RELAY: case COMMS_CONN_RELAY:
if ( NetStateCache.netAvail( m_context ) ) { if ( XWPrefs.getUDPEnabled( m_context ) ) {
putOut( buf ); // add to queue nSent = RelayService.sendPacket( m_context, m_rowid, buf );
if ( null == m_thread ) { } else {
m_thread = new CommsThread(); if ( NetStateCache.netAvail( m_context ) ) {
m_thread.start(); putOut( buf ); // add to queue
if ( null == m_thread ) {
m_thread = new CommsThread();
m_thread.start();
}
nSent = buf.length;
} }
nSent = buf.length;
} }
break; break;
case COMMS_CONN_SMS: case COMMS_CONN_SMS:

View file

@ -265,6 +265,11 @@ public class DlgDelegate {
(String)args[0] ); (String)args[0] );
asToast = false; asToast = false;
break; break;
case RELAY_ALERT:
msg = (String)args[0];
asToast = false;
break;
default: default:
DbgUtils.logf( "eventOccurred: unhandled event %s", event.toString() ); DbgUtils.logf( "eventOccurred: unhandled event %s", event.toString() );
} }
@ -452,9 +457,17 @@ public class DlgDelegate {
private void addState( DlgState state ) private void addState( DlgState state )
{ {
// I'm getting serialization failures on devices pointing at
// DlgState but the code below says the object's fine (as it
// should be.) Just to have a record....
//
// Bundle bundle = new Bundle();
// DbgUtils.logf( "addState: testing serializable" );
// bundle.putSerializable( "foo", state );
// state = (DlgState)bundle.getSerializable( "foo" );
// DbgUtils.logf( "addState: serializable is ok" );
m_dlgStates.put( state.m_id, state ); m_dlgStates.put( state.m_id, state );
// DbgUtils.logf( "addState: there are now %d active dialogs",
// m_dlgStates.size() );
} }
} }

View file

@ -58,40 +58,44 @@ public class GCMIntentService extends GCMBaseIntentService {
protected void onMessage( Context context, Intent intent ) protected void onMessage( Context context, Intent intent )
{ {
String value; String value;
boolean ignoreIt = XWPrefs.getGCMIgnored( this );
if ( ignoreIt ) {
DbgUtils.logf( "received GCM but ignoring it" );
} else {
value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) {
UpdateCheckReceiver.checkVersions( context, true );
}
value = intent.getStringExtra( "getMoves" ); value = intent.getStringExtra( "getMoves" );
if ( null != value && Boolean.parseBoolean( value ) ) { if ( null != value && Boolean.parseBoolean( value ) ) {
RelayReceiver.RestartTimer( context, true ); RelayReceiver.RestartTimer( context, true );
} }
value = intent.getStringExtra( "msgs64" ); value = intent.getStringExtra( "msgs64" );
if ( null != value ) { if ( null != value ) {
String connname = intent.getStringExtra( "connname" ); String connname = intent.getStringExtra( "connname" );
if ( null != connname ) { if ( null != connname ) {
try { try {
JSONArray msgs64 = new JSONArray( value ); JSONArray msgs64 = new JSONArray( value );
String[] strs64 = new String[msgs64.length()]; String[] strs64 = new String[msgs64.length()];
for ( int ii = 0; ii < strs64.length; ++ii ) { for ( int ii = 0; ii < strs64.length; ++ii ) {
strs64[ii] = msgs64.optString(ii); strs64[ii] = msgs64.optString(ii);
}
RelayService.processMsgs( context, connname, strs64 );
} catch (org.json.JSONException jse ) {
DbgUtils.loge( jse );
} }
RelayService.processMsgs( context, connname, strs64 );
} catch (org.json.JSONException jse ) {
DbgUtils.loge( jse );
} }
} }
}
value = intent.getStringExtra( "checkUpdates" ); value = intent.getStringExtra( "msg" );
if ( null != value && Boolean.parseBoolean( value ) ) { if ( null != value ) {
UpdateCheckReceiver.checkVersions( context, true ); String title = intent.getStringExtra( "title" );
} if ( null != title ) {
int code = value.hashCode() ^ title.hashCode();
value = intent.getStringExtra( "msg" ); Utils.postNotification( context, null, title, value, code );
if ( null != value ) { }
String title = intent.getStringExtra( "title" );
if ( null != title ) {
int code = value.hashCode() ^ title.hashCode();
Utils.postNotification( context, null, title, value, code );
} }
} }
} }

View file

@ -74,8 +74,9 @@ public class GamesList extends XWExpandableListActivity
private static final String SAVE_DICTNAMES = "SAVE_DICTNAMES"; private static final String SAVE_DICTNAMES = "SAVE_DICTNAMES";
private static final String RELAYIDS_EXTRA = "relayids"; private static final String RELAYIDS_EXTRA = "relayids";
private static final String ROWID_EXTRA = "rowid";
private static final String GAMEID_EXTRA = "gameid"; private static final String GAMEID_EXTRA = "gameid";
private static final String REMATCH_ROWID_EXTRA = "rowid"; private static final String REMATCH_ROWID_EXTRA = "rowid_rm";
private static final int NEW_NET_GAME_ACTION = 1; private static final int NEW_NET_GAME_ACTION = 1;
private static final int RESET_GAME_ACTION = 2; private static final int RESET_GAME_ACTION = 2;
@ -388,6 +389,7 @@ public class GamesList extends XWExpandableListActivity
m_gameLaunched = false; m_gameLaunched = false;
Assert.assertNotNull( intent ); Assert.assertNotNull( intent );
invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) ); invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) );
invalRowID( intent.getLongExtra( ROWID_EXTRA, -1 ) );
startFirstHasDict( intent ); startFirstHasDict( intent );
startNewNetGame( intent ); startNewNetGame( intent );
startHasGameID( intent ); startHasGameID( intent );
@ -919,10 +921,18 @@ public class GamesList extends XWExpandableListActivity
} }
} }
private void invalRowID( long rowid )
{
if ( -1 != rowid ) {
m_adapter.inval( rowid );
}
}
// Launch the first of these for which there's a dictionary // Launch the first of these for which there's a dictionary
// present. // present.
private void startFirstHasDict( String[] relayIDs ) private boolean startFirstHasDict( String[] relayIDs )
{ {
boolean launched = false;
if ( null != relayIDs ) { if ( null != relayIDs ) {
outer: outer:
for ( String relayID : relayIDs ) { for ( String relayID : relayIDs ) {
@ -931,19 +941,33 @@ public class GamesList extends XWExpandableListActivity
for ( long rowid : rowids ) { for ( long rowid : rowids ) {
if ( GameUtils.gameDictsHere( this, rowid ) ) { if ( GameUtils.gameDictsHere( this, rowid ) ) {
launchGame( rowid ); launchGame( rowid );
launched = true;
break outer; break outer;
} }
} }
} }
} }
} }
return launched;
}
private void startFirstHasDict( long rowid )
{
if ( -1 != rowid ) {
if ( GameUtils.gameDictsHere( this, rowid ) ) {
launchGame( rowid );
}
}
} }
private void startFirstHasDict( Intent intent ) private void startFirstHasDict( Intent intent )
{ {
if ( null != intent ) { if ( null != intent ) {
String[] relayIDs = intent.getStringArrayExtra( RELAYIDS_EXTRA ); String[] relayIDs = intent.getStringArrayExtra( RELAYIDS_EXTRA );
startFirstHasDict( relayIDs ); if ( !startFirstHasDict( relayIDs ) ) {
long rowid = intent.getLongExtra( ROWID_EXTRA, -1 );
startFirstHasDict( rowid );
}
} }
} }
@ -1099,11 +1123,10 @@ public class GamesList extends XWExpandableListActivity
return intent; return intent;
} }
public static Intent makeRelayIdsIntent( Context context, public static Intent makeRowidIntent( Context context, long rowid )
String[] relayIDs )
{ {
Intent intent = makeSelfIntent( context ); Intent intent = makeSelfIntent( context );
intent.putExtra( RELAYIDS_EXTRA, relayIDs ); intent.putExtra( ROWID_EXTRA, rowid );
return intent; return intent;
} }

View file

@ -36,7 +36,7 @@ public class MultiMsgSink implements TransportProcs {
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID ) public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
{ {
Assert.fail(); Assert.fail(); // implement if this is getting called!!!
return -1; return -1;
} }
@ -53,9 +53,9 @@ public class MultiMsgSink implements TransportProcs {
{ {
} }
public boolean relayNoConnProc( byte[] buf, String relayID ) public boolean relayNoConnProc( byte[] buf, String relayID )
{ {
Assert.fail(); Assert.fail(); // implement if this is getting called!!!
return false; return false;
} }
} }

View file

@ -62,6 +62,8 @@ public class MultiService {
, SMS_SEND_OK , SMS_SEND_OK
, SMS_SEND_FAILED , SMS_SEND_FAILED
, SMS_SEND_FAILED_NORADIO , SMS_SEND_FAILED_NORADIO
, RELAY_ALERT
}; };
public interface MultiEventListener { public interface MultiEventListener {

View file

@ -41,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_udpEnabled;
private String m_downloadPath; private String m_downloadPath;
@Override @Override
@ -122,6 +123,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_udpEnabled = getString( R.string.key_udp_relay );
m_downloadPath = getString( R.string.key_download_path ); m_downloadPath = getString( R.string.key_download_path );
Button button = (Button)findViewById( R.id.revert_colors ); Button button = (Button)findViewById( R.id.revert_colors );
@ -161,6 +163,8 @@ public class PrefsActivity extends PreferenceActivity
DbgUtils.logEnable( sp.getBoolean( key, false ) ); DbgUtils.logEnable( sp.getBoolean( key, false ) );
} else if ( key.equals( m_smsToasting ) ) { } else if ( key.equals( m_smsToasting ) ) {
SMSService.smsToastEnable( sp.getBoolean( key, false ) ); SMSService.smsToastEnable( sp.getBoolean( key, false ) );
} else if ( key.equals( m_udpEnabled ) ) {
RelayService.udpChanged( this );
} else if ( key.equals( m_smsEnable ) ) { } else if ( key.equals( m_smsEnable ) ) {
if ( sp.getBoolean( key, true ) ) { if ( sp.getBoolean( key, true ) ) {
SMSService.checkForInvites( this ); SMSService.checkForInvites( this );

View file

@ -25,24 +25,113 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.IBinder; import android.os.IBinder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;
import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.GameSummary; import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.UtilCtxt;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
public class RelayService extends Service { public class RelayService extends XWService {
private static final int MAX_SEND = 1024; private static final int MAX_SEND = 1024;
private static final int MAX_BUF = MAX_SEND - 2; private static final int MAX_BUF = MAX_SEND - 2;
private static final String CMD_STR = "CMD"; private static final String CMD_STR = "CMD";
private static final int PROCESS_MSGS = 1; private static final int PROCESS_MSGS = 1;
private static final int UDP_CHANGED = 2;
private static final int SEND = 3;
private static final int RECEIVE = 4;
private static final String MSGS = "MSGS"; private static final String MSGS = "MSGS";
private static final String RELAY_ID = "RELAY_ID"; private static final String RELAY_ID = "RELAY_ID";
private static final String ROWID = "ROWID";
private static final String BINBUFFER = "BINBUFFER";
private static HashSet<Integer> s_packetsSent = new HashSet<Integer>();
private static int s_nextPacketID = 1;
private Thread m_fetchThread = null;
private Thread m_UDPReadThread = null;
private Thread m_UDPWriteThread = null;
private DatagramSocket m_UDPSocket;
private LinkedBlockingQueue<DatagramPacket> m_queue = null;
// These must match the enum XWRelayReg in xwrelay.h
private static final int XWPDEV_PROTO_VERSION = 0;
// private static final int XWPDEV_NONE = 0;
private enum XWRelayReg {
XWPDEV_NONE
,XWPDEV_ALERT
,XWPDEV_REG
,XWPDEV_REGRSP
,XWPDEV_PING
,XWPDEV_HAVEMSGS
,XWPDEV_RQSTMSGS
,XWPDEV_MSG
,XWPDEV_MSGNOCONN
,XWPDEV_MSGRSP
,XWPDEV_BADREG
,XWPDEV_ACK
};
// private static final int XWPDEV_ALERT = 1;
// private static final int XWPDEV_REG = 2;
// private static final int XWPDEV_REGRSP = 3;
// private static final int XWPDEV_PING = 4;
// private static final int XWPDEV_HAVEMSGS = 5;
// private static final int XWPDEV_RQSTMSGS = 6;
// private static final int XWPDEV_MSG = 7;
// private static final int XWPDEV_MSGNOCONN = 8;
// private static final int XWPDEV_MSGRSP = 9;
// private static final int XWPDEV_BADREG = 10;
public static void startService( Context context )
{
Intent intent = getIntentTo( context, UDP_CHANGED );
context.startService( intent );
}
public static int sendPacket( Context context, long rowid, byte[] buf )
{
Intent intent = getIntentTo( context, SEND )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, buf );
context.startService( intent );
return buf.length;
}
// Exists to get incoming data onto the main thread
private static void postData( Context context, long rowid, byte[] msg )
{
DbgUtils.logf( "RelayService::postData: packet of length %d for token %d",
msg.length, rowid );
Intent intent = getIntentTo( context, RECEIVE )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, msg );
context.startService( intent );
}
public static void udpChanged( Context context )
{
startService( context );
}
public static void processMsgs( Context context, String relayId, public static void processMsgs( Context context, String relayId,
String[] msgs64 ) String[] msgs64 )
@ -64,45 +153,65 @@ public class RelayService extends Service {
public void onCreate() public void onCreate()
{ {
super.onCreate(); super.onCreate();
startFetchThreadIf();
Thread thread = new Thread( null, new Runnable() {
public void run() {
fetchAndProcess();
RelayService.this.stopSelf();
}
}, getClass().getName() );
thread.start();
}
@Override
public IBinder onBind( Intent intent )
{
return null;
} }
@Override @Override
public int onStartCommand( Intent intent, int flags, int startId ) public int onStartCommand( Intent intent, int flags, int startId )
{ {
int cmd = intent.getIntExtra( CMD_STR, -1 ); DbgUtils.logf( "RelayService::onStartCommand" );
switch( cmd ) { int result;
case PROCESS_MSGS: if ( null != intent ) {
String[] relayIDs = new String[1]; int cmd = intent.getIntExtra( CMD_STR, -1 );
relayIDs[0] = intent.getStringExtra( RELAY_ID ); switch( cmd ) {
long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] ); case -1:
if ( 0 < rowIDs.length ) { break;
String[] msgs64 = intent.getStringArrayExtra( MSGS ); case PROCESS_MSGS:
int count = msgs64.length; String[] relayIDs = new String[1];
relayIDs[0] = intent.getStringExtra( RELAY_ID );
long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
if ( 0 < rowIDs.length ) {
String[] msgs64 = intent.getStringArrayExtra( MSGS );
int count = msgs64.length;
byte[][][] msgs = new byte[1][count][]; byte[][][] msgs = new byte[1][count][];
for ( int ii = 0; ii < count; ++ii ) { for ( int ii = 0; ii < count; ++ii ) {
msgs[0][ii] = XwJNI.base64Decode( msgs64[ii] ); msgs[0][ii] = XwJNI.base64Decode( msgs64[ii] );
}
process( msgs, rowIDs, relayIDs );
} }
process( msgs, rowIDs, relayIDs ); break;
case UDP_CHANGED:
DbgUtils.logf( "RelayService::onStartCommand::UDP_CHANGED" );
if ( XWPrefs.getUDPEnabled( this ) ) {
stopFetchThreadIf();
startUDPThreadsIfNot();
registerWithRelay();
} else {
stopUDPThreadsIf();
startFetchThreadIf();
}
break;
case SEND:
case RECEIVE:
startUDPThreadsIfNot();
long rowid = intent.getLongExtra( ROWID, -1 );
byte[] msg = intent.getByteArrayExtra( BINBUFFER );
if ( SEND == cmd ) {
sendMessage( rowid, msg );
} else {
feedMessage( rowid, msg );
}
break;
default:
Assert.fail();
} }
break;
result = Service.START_STICKY;
} else {
result = Service.START_STICKY_COMPATIBILITY;
} }
stopSelf( startId ); return result;
return Service.START_NOT_STICKY;
} }
private void setupNotification( String[] relayIDs ) private void setupNotification( String[] relayIDs )
@ -111,18 +220,380 @@ public class RelayService extends Service {
long[] rowids = DBUtils.getRowIDsFor( this, relayID ); long[] rowids = DBUtils.getRowIDsFor( this, relayID );
if ( null != rowids ) { if ( null != rowids ) {
for ( long rowid : rowids ) { for ( long rowid : rowids ) {
Intent intent = setupNotification( rowid );
GamesList.makeRelayIdsIntent( this,
new String[] {relayID} );
String msg = Utils.format( this, R.string.notify_bodyf,
GameUtils.getName( this, rowid ) );
Utils.postNotification( this, intent, R.string.notify_title,
msg, (int)rowid );
} }
} }
} }
} }
private void setupNotification( long rowid )
{
Intent intent = GamesList.makeRowidIntent( this, rowid );
String msg = Utils.format( this, R.string.notify_bodyf,
GameUtils.getName( this, rowid ) );
Utils.postNotification( this, intent, R.string.notify_title,
msg, (int)rowid );
}
private void startFetchThreadIf()
{
DbgUtils.logf( "startFetchThreadIf()" );
if ( !XWPrefs.getUDPEnabled( this ) && null == m_fetchThread ) {
m_fetchThread = new Thread( null, new Runnable() {
public void run() {
fetchAndProcess();
m_fetchThread = null;
RelayService.this.stopSelf();
}
}, getClass().getName() );
m_fetchThread.start();
}
}
private void stopFetchThreadIf()
{
if ( null != m_fetchThread ) {
DbgUtils.logf( "2: m_fetchThread NOT NULL; WHAT TO DO???" );
}
}
private void startUDPThreadsIfNot()
{
if ( XWPrefs.getUDPEnabled( this ) ) {
if ( null == m_UDPSocket ) {
int port = XWPrefs.getDefaultRelayPort( RelayService.this );
String host = XWPrefs.getDefaultRelayHost( RelayService.this );
try {
m_UDPSocket = new DatagramSocket();
InetAddress addr = InetAddress.getByName( host );
m_UDPSocket.connect( addr, port ); // remember this address
} catch( java.net.SocketException se ) {
DbgUtils.loge( se );
Assert.fail();
} catch( java.net.UnknownHostException uhe ) {
DbgUtils.loge( uhe );
}
} else {
Assert.assertTrue( m_UDPSocket.isConnected() );
DbgUtils.logf( "m_UDPSocket not null" );
}
if ( null == m_UDPReadThread ) {
m_UDPReadThread = new Thread( null, new Runnable() {
public void run() {
DbgUtils.logf( "read thread running" );
byte[] buf = new byte[1024];
for ( ; ; ) {
DatagramPacket packet =
new DatagramPacket( buf, buf.length );
try {
DbgUtils.logf( "UPD read thread blocking on receive" );
m_UDPSocket.receive( packet );
DbgUtils.logf( "UPD read thread: receive returned" );
} catch( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
break; // ???
}
DbgUtils.logf( "received %d bytes", packet.getLength() );
gotPacket( packet );
}
DbgUtils.logf( "read thread exiting" );
}
}, getClass().getName() );
m_UDPReadThread.start();
} else {
DbgUtils.logf( "m_UDPReadThread not null and assumed to be running" );
}
if ( null == m_UDPWriteThread ) {
m_queue = new LinkedBlockingQueue<DatagramPacket>();
m_UDPWriteThread = new Thread( null, new Runnable() {
public void run() {
DbgUtils.logf( "write thread running" );
for ( ; ; ) {
DatagramPacket outPacket;
try {
outPacket = m_queue.take();
} catch ( InterruptedException ie ) {
DbgUtils.logf( "RelayService; write thread killed" );
break;
}
if ( null == outPacket || 0 == outPacket.getLength() ) {
DbgUtils.logf( "stopping write thread" );
break;
}
DbgUtils.logf( "Sending udp packet of length %d",
outPacket.getLength() );
try {
m_UDPSocket.send( outPacket );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
DbgUtils.logf( "write thread exiting" );
}
}, getClass().getName() );
m_UDPWriteThread.start();
} else {
DbgUtils.logf( "m_UDPWriteThread not null and assumed to be running" );
}
}
}
private void stopUDPThreadsIf()
{
DbgUtils.logf( "stopUDPThreadsIf" );
if ( null != m_queue && null != m_UDPWriteThread ) {
// can't add null
m_queue.add( new DatagramPacket( new byte[0], 0 ) );
try {
DbgUtils.logf( "joining m_UDPWriteThread" );
m_UDPWriteThread.join();
DbgUtils.logf( "SUCCESSFULLY joined m_UDPWriteThread" );
} catch( java.lang.InterruptedException ie ) {
DbgUtils.loge( ie );
}
m_UDPWriteThread = null;
m_queue = null;
}
if ( null != m_UDPSocket && null != m_UDPReadThread ) {
m_UDPSocket.close();
DbgUtils.logf( "waiting for read thread to exit" );
try {
m_UDPReadThread.join();
} catch( java.lang.InterruptedException ie ) {
DbgUtils.loge( ie );
}
DbgUtils.logf( "read thread exited" );
m_UDPReadThread = null;
m_UDPSocket = null;
}
DbgUtils.logf( "stopUDPThreadsIf DONE" );
}
// Running on reader thread
private void gotPacket( DatagramPacket packet )
{
int packetLen = packet.getLength();
byte[] data = new byte[packetLen];
System.arraycopy( packet.getData(), 0, data, 0, packetLen );
DbgUtils.logf( "RelayService::gotPacket: %d bytes of data", packetLen );
ByteArrayInputStream bis = new ByteArrayInputStream( data );
DataInputStream dis = new DataInputStream( bis );
try {
PacketHeader header = readHeader( dis );
if ( null != header ) {
sendAckIf( header );
switch ( header.m_cmd ) {
case XWPDEV_ALERT:
String str = getStringWithLength( dis );
sendResult( MultiEvent.RELAY_ALERT, str );
break;
case XWPDEV_BADREG:
str = getStringWithLength( dis );
DbgUtils.logf( "bad relayID \"%s\" reported", str );
XWPrefs.clearRelayDevID( this );
registerWithRelay();
break;
case XWPDEV_REGRSP:
DbgUtils.logf( "got XWPDEV_REGRSP" );
str = getStringWithLength( dis );
DbgUtils.logf( "got relayid %s", str );
XWPrefs.setRelayDevID( this, str );
break;
case XWPDEV_HAVEMSGS:
requestMessages();
break;
case XWPDEV_MSG:
DbgUtils.logf( "got XWPDEV_MSG" );
int token = dis.readInt();
byte[] msg = new byte[dis.available()];
Assert.assertTrue( packet.getLength() >= msg.length );
Assert.assertTrue( packetLen >= msg.length );
dis.read( msg );
postData( RelayService.this, token, msg );
break;
case XWPDEV_ACK:
noteAck( dis.readInt() );
break;
default:
DbgUtils.logf( "RelayService: Unhandled cmd: %d",
header.m_cmd );
break;
}
}
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
} // gotPacket
private void registerWithRelay()
{
DbgUtils.logf( "registerWithRelay" );
byte[] typ = new byte[1];
String devid = getDevID(typ);
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DataOutputStream out = addProtoAndCmd( bas, XWRelayReg.XWPDEV_REG );
out.writeByte( typ[0] );
out.writeShort( devid.length() );
out.writeBytes( devid );
postPacket( bas );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
private void requestMessages()
{
DbgUtils.logf( "requestMessages" );
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DataOutputStream out =
addProtoAndCmd( bas, XWRelayReg.XWPDEV_RQSTMSGS );
String devid = getDevID( null );
out.writeShort( devid.length() );
out.writeBytes( devid );
postPacket( bas );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
private void sendMessage( long rowid, byte[] msg )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DataOutputStream out = addProtoAndCmd( bas, XWRelayReg.XWPDEV_MSG );
Assert.assertTrue( rowid < Integer.MAX_VALUE );
out.writeInt( (int)rowid );
out.write( msg, 0, msg.length );
postPacket( bas );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
private void sendNoConnMessage( long rowid, String relayID, byte[] msg )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DataOutputStream out =
addProtoAndCmd( bas, XWRelayReg.XWPDEV_MSGNOCONN );
Assert.assertTrue( rowid < Integer.MAX_VALUE );
out.writeInt( (int)rowid );
out.writeBytes( relayID );
out.write( '\n' );
out.write( msg, 0, msg.length );
postPacket( bas );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
private void sendAckIf( PacketHeader header )
{
DbgUtils.logf( "sendAckIf" );
if ( XWRelayReg.XWPDEV_ACK != header.m_cmd ) {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DataOutputStream out =
addProtoAndCmd( bas, XWRelayReg.XWPDEV_ACK );
out.writeInt( header.m_packetID );
postPacket( bas );
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
}
}
private PacketHeader readHeader( DataInputStream dis )
throws java.io.IOException
{
PacketHeader result = null;
byte proto = dis.readByte();
if ( XWPDEV_PROTO_VERSION == proto ) {
int packetID = dis.readInt();
DbgUtils.logf( "readHeader: got packetID %d", packetID );
byte ordinal = dis.readByte();
XWRelayReg cmd = XWRelayReg.values()[ordinal];
result = new PacketHeader( cmd, packetID );
} else {
DbgUtils.logf( "bad proto: %d", proto );
}
DbgUtils.logf( "readHeader => %H", result );
return result;
}
private String getStringWithLength( DataInputStream dis )
throws java.io.IOException
{
short len = dis.readShort();
byte[] tmp = new byte[len];
dis.read( tmp );
return new String( tmp );
}
private DataOutputStream addProtoAndCmd( ByteArrayOutputStream bas,
XWRelayReg cmd )
throws java.io.IOException
{
DataOutputStream out = new DataOutputStream( bas );
out.writeByte( XWPDEV_PROTO_VERSION );
out.writeInt( nextPacketID( cmd ) ); // packetID
out.writeByte( cmd.ordinal() );
return out;
}
private void postPacket( ByteArrayOutputStream bas )
{
byte[] data = bas.toByteArray();
m_queue.add( new DatagramPacket( data, data.length ) );
}
private String getDevID( byte[] typp )
{
byte typ;
String devid = XWPrefs.getRelayDevID( this );
if ( null != devid && 0 < devid.length() ) {
typ = UtilCtxt.ID_TYPE_RELAY;
} else {
devid = XWPrefs.getGCMDevID( this );
if ( null != devid && 0 < devid.length() ) {
typ = UtilCtxt.ID_TYPE_ANDROID_GCM;
} else {
devid = "";
typ = UtilCtxt.ID_TYPE_ANON;
}
}
if ( null != typp ) {
typp[0] = typ;
} else {
Assert.assertTrue( typ == UtilCtxt.ID_TYPE_RELAY );
}
return devid;
}
private void feedMessage( long rowid, byte[] msg )
{
DbgUtils.logf( "RelayService::feedMessage: %d bytes for rowid %d",
msg.length, rowid );
if ( BoardActivity.feedMessage( rowid, msg ) ) {
DbgUtils.logf( "feedMessage: board ate it" );
// do nothing
} else {
RelayMsgSink sink = new RelayMsgSink();
sink.setRowID( rowid );
if ( GameUtils.feedMessage( this, rowid, msg, null,
sink ) ) {
setupNotification( rowid );
} else {
DbgUtils.logf( "feedMessage: background dropped it" );
}
}
}
private void fetchAndProcess() private void fetchAndProcess()
{ {
long[][] rowIDss = new long[1][]; long[][] rowIDss = new long[1][];
@ -256,29 +727,79 @@ public class RelayService extends Service {
private class RelayMsgSink extends MultiMsgSink { private class RelayMsgSink extends MultiMsgSink {
private HashMap<String,ArrayList<byte[]>> m_msgLists = null; private HashMap<String,ArrayList<byte[]>> m_msgLists = null;
private long m_rowid = -1;
public void setRowID( long rowid ) { m_rowid = rowid; }
public void send( Context context ) public void send( Context context )
{ {
sendToRelay( context, m_msgLists ); if ( -1 == m_rowid ) {
sendToRelay( context, m_msgLists );
} else {
Assert.assertNull( m_msgLists );
}
} }
/***** TransportProcs interface *****/ /***** TransportProcs interface *****/
public int transportSend( byte[] buf, final CommsAddrRec addr,
int gameID )
{
Assert.assertTrue( -1 != m_rowid );
sendPacket( RelayService.this, m_rowid, buf );
return buf.length;
}
public boolean relayNoConnProc( byte[] buf, String relayID ) public boolean relayNoConnProc( byte[] buf, String relayID )
{ {
if ( null == m_msgLists ) { if ( -1 != m_rowid ) {
m_msgLists = new HashMap<String,ArrayList<byte[]>>(); sendNoConnMessage( m_rowid, relayID, buf );
} } else {
if ( null == m_msgLists ) {
m_msgLists = new HashMap<String,ArrayList<byte[]>>();
}
ArrayList<byte[]> list = m_msgLists.get( relayID ); ArrayList<byte[]> list = m_msgLists.get( relayID );
if ( list == null ) { if ( list == null ) {
list = new ArrayList<byte[]>(); list = new ArrayList<byte[]>();
m_msgLists.put( relayID, list ); m_msgLists.put( relayID, list );
}
list.add( buf );
} }
list.add( buf );
return true; return true;
} }
} }
private static int nextPacketID( XWRelayReg cmd )
{
int nextPacketID = 0;
synchronized( s_packetsSent ) {
if ( XWRelayReg.XWPDEV_ACK != cmd ) {
nextPacketID = s_nextPacketID++;
s_packetsSent.add( nextPacketID );
}
}
DbgUtils.logf( "nextPacketID(%s)=>%d", cmd.toString(), nextPacketID );
return nextPacketID;
}
private static void noteAck( int packetID )
{
synchronized( s_packetsSent ) {
s_packetsSent.remove( packetID );
DbgUtils.logf( "Got ack for %d; there are %d unacked packets",
packetID, s_packetsSent.size() );
}
}
private class PacketHeader {
public int m_packetID;
public XWRelayReg m_cmd;
public PacketHeader( XWRelayReg cmd, int packetID ) {
DbgUtils.logf( "in PacketHeader contructor" );
m_packetID = packetID;
m_cmd = cmd;
}
}
} }

View file

@ -32,7 +32,6 @@ import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.telephony.SmsManager; import android.telephony.SmsManager;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
@ -52,7 +51,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
public class SMSService extends Service { public class SMSService extends XWService {
private static final String INSTALL_URL = "http://eehouse.org/_/a.py/a "; 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 MAX_SMS_LEN = 140; // ??? differs by network
@ -66,7 +65,7 @@ public class SMSService extends Service {
private static final int INVITE = 2; private static final int INVITE = 2;
private static final int SEND = 3; private static final int SEND = 3;
private static final int REMOVE = 4; private static final int REMOVE = 4;
private static final int MESG_GAMEGONE = 5; // private static final int MESG_GAMEGONE = 5;
private static final int CHECK_MSGDB = 6; private static final int CHECK_MSGDB = 6;
private static final int ADDED_MISSING = 7; private static final int ADDED_MISSING = 7;
private static final int STOP_SELF = 8; private static final int STOP_SELF = 8;
@ -77,7 +76,6 @@ public class SMSService extends Service {
private static final String PHONE = "PHONE"; private static final String PHONE = "PHONE";
private static Boolean s_showToasts = null; private static Boolean s_showToasts = null;
private static MultiService s_srcMgr = null;
// All messages are base64-encoded byte arrays. The first byte is // All messages are base64-encoded byte arrays. The first byte is
// always one of these. What follows depends. // always one of these. What follows depends.
@ -196,16 +194,6 @@ public class SMSService extends Service {
return result; return result;
} }
public static void setListener( MultiService.MultiEventListener li )
{
if ( XWApp.SMSSUPPORTED ) {
if ( null == s_srcMgr ) {
s_srcMgr = new MultiService();
}
s_srcMgr.setListener( li );
}
}
private static Intent getIntentTo( Context context, int cmd ) private static Intent getIntentTo( Context context, int cmd )
{ {
if ( null == s_showToasts ) { if ( null == s_showToasts ) {
@ -303,12 +291,6 @@ public class SMSService extends Service {
return result; return result;
} // onStartCommand } // onStartCommand
@Override
public IBinder onBind( Intent intent )
{
return null;
}
private void inviteRemote( String phone, int gameID, String gameName, private void inviteRemote( String phone, int gameID, String gameName,
int lang, String dict, int lang, String dict,
int nPlayersT, int nPlayersH ) int nPlayersT, int nPlayersH )
@ -662,13 +644,6 @@ public class SMSService extends Service {
} }
} }
private void sendResult( MultiEvent event, Object ... args )
{
if ( null != s_srcMgr ) {
s_srcMgr.sendResult( event, args );
}
}
private void registerReceivers() private void registerReceivers()
{ {
registerReceiver( new BroadcastReceiver() { registerReceiver( new BroadcastReceiver() {

View file

@ -54,8 +54,7 @@ public class XWActivity extends Activity
protected void onResume() protected void onResume()
{ {
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setListener( this ); XWService.setListener( this );
SMSService.setListener( this );
super.onResume(); super.onResume();
} }
@ -63,8 +62,7 @@ public class XWActivity extends Activity
protected void onPause() protected void onPause()
{ {
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setListener( null ); XWService.setListener( null );
SMSService.setListener( null );
super.onPause(); super.onPause();
} }

View file

@ -58,10 +58,10 @@ public class XWApp extends Application {
RelayReceiver.RestartTimer( this ); RelayReceiver.RestartTimer( this );
UpdateCheckReceiver.restartTimer( this ); UpdateCheckReceiver.restartTimer( this );
BTService.startService( this ); BTService.startService( this );
SMSService.checkForInvites( this ); SMSService.checkForInvites( this );
RelayService.startService( this );
GCMIntentService.init( this ); GCMIntentService.init( this );
} }

View file

@ -41,6 +41,22 @@ public class XWExpandableListActivity extends ExpandableListActivity
m_delegate = new DlgDelegate( this, this, savedInstanceState ); m_delegate = new DlgDelegate( this, this, savedInstanceState );
} }
@Override
protected void onResume()
{
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
XWService.setListener( this );
super.onResume();
}
@Override
protected void onPause()
{
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
XWService.setListener( null );
super.onPause();
}
@Override @Override
protected void onSaveInstanceState( Bundle outState ) protected void onSaveInstanceState( Bundle outState )
{ {

View file

@ -51,8 +51,7 @@ public class XWListActivity extends ListActivity
protected void onResume() protected void onResume()
{ {
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setListener( this ); XWService.setListener( this );
SMSService.setListener( this );
super.onResume(); super.onResume();
} }
@ -60,8 +59,7 @@ public class XWListActivity extends ListActivity
protected void onPause() protected void onPause()
{ {
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setListener( null ); XWService.setListener( null );
SMSService.setListener( null );
super.onPause(); super.onPause();
} }

View file

@ -34,6 +34,16 @@ public class XWPrefs {
return getPrefsBoolean( context, R.string.key_enable_sms, false ); return getPrefsBoolean( context, R.string.key_enable_sms, false );
} }
public static boolean getUDPEnabled( Context context )
{
return getPrefsBoolean( context, R.string.key_udp_relay, false );
}
public static boolean getGCMIgnored( Context context )
{
return getPrefsBoolean( context, R.string.key_drop_gcm, false );
}
public static boolean getDebugEnabled( Context context ) public static boolean getDebugEnabled( Context context )
{ {
return getPrefsBoolean( context, R.string.key_enable_debug, false ); return getPrefsBoolean( context, R.string.key_enable_debug, false );

View file

@ -0,0 +1,60 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2010 - 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) 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;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import org.eehouse.android.xw4.MultiService.MultiEvent;
public class XWService extends Service {
protected static MultiService s_srcMgr = null;
@Override
public IBinder onBind( Intent intent )
{
return null;
}
public final static void setListener( MultiService.MultiEventListener li )
{
if ( null == s_srcMgr ) {
DbgUtils.logf( "setListener: registering %s", li.getClass().getName() );
s_srcMgr = new MultiService();
}
s_srcMgr.setListener( li );
}
protected void sendResult( MultiEvent event, Object ... args )
{
if ( null != s_srcMgr ) {
s_srcMgr.sendResult( event, args );
} else {
DbgUtils.logf( "sendResult: dropping event" );
}
}
}

View file

@ -286,6 +286,9 @@ public class JNIThread extends Thread {
// state. In some cases it'll otherwise drop the move. // state. In some cases it'll otherwise drop the move.
XwJNI.server_do( m_jniGamePtr ); XwJNI.server_do( m_jniGamePtr );
// And let it tell the relay (if any) it's leaving
XwJNI.comms_stop( m_jniGamePtr );
XwJNI.game_getGi( m_jniGamePtr, m_gi ); XwJNI.game_getGi( m_jniGamePtr, m_gi );
if ( null != m_newDict ) { if ( null != m_newDict ) {
m_gi.dictName = m_newDict; m_gi.dictName = m_newDict;

View file

@ -60,6 +60,8 @@ public interface UtilCtxt {
public static final int ID_TYPE_NONE = 0; public static final int ID_TYPE_NONE = 0;
public static final int ID_TYPE_RELAY = 1; public static final int ID_TYPE_RELAY = 1;
public static final int ID_TYPE_ANDROID_GCM = 3; public static final int ID_TYPE_ANDROID_GCM = 3;
public static final int ID_TYPE_ANDROID_OTHER = 4;
public static final int ID_TYPE_ANON = 5;
String getDevID( /*out*/ byte[] typ ); String getDevID( /*out*/ byte[] typ );
void deviceRegistered( int devIDType, String idRelay ); void deviceRegistered( int devIDType, String idRelay );

View file

@ -234,6 +234,7 @@ public class XwJNI {
// Comms // Comms
public static native void comms_start( int gamePtr ); public static native void comms_start( int gamePtr );
public static native void comms_stop( int gamePtr );
public static native void comms_resetSame( int gamePtr ); public static native void comms_resetSame( int gamePtr );
public static native void comms_getAddr( int gamePtr, CommsAddrRec addr ); public static native void comms_getAddr( int gamePtr, CommsAddrRec addr );
public static native CommsAddrRec[] comms_getAddrs( int gamePtr ); public static native CommsAddrRec[] comms_getAddrs( int gamePtr );

View file

@ -467,8 +467,11 @@ comms_transportFailed( CommsCtxt* comms )
void void
comms_destroy( CommsCtxt* comms ) comms_destroy( CommsCtxt* comms )
{ {
CommsAddrRec aNew; /* did I call comms_stop()? */
aNew.conType = COMMS_CONN_NONE; XP_ASSERT( COMMS_CONN_RELAY != comms->addr.conType
|| COMMS_RELAYSTATE_UNCONNECTED == comms->r.relayState );
CommsAddrRec aNew = { .conType = COMMS_CONN_NONE };
util_addrChange( comms->util, &comms->addr, &aNew ); util_addrChange( comms->util, &comms->addr, &aNew );
cleanupInternal( comms ); cleanupInternal( comms );
@ -665,6 +668,14 @@ comms_start( CommsCtxt* comms )
sendConnect( comms, XP_FALSE ); sendConnect( comms, XP_FALSE );
} /* comms_start */ } /* comms_start */
void
comms_stop( CommsCtxt* comms )
{
if ( COMMS_CONN_RELAY == comms->addr.conType ) {
relayDisconnect( comms );
}
}
static void static void
sendConnect( CommsCtxt* comms, XP_Bool breakExisting ) sendConnect( CommsCtxt* comms, XP_Bool breakExisting )
{ {
@ -1185,6 +1196,14 @@ gameID( const CommsCtxt* comms )
gameID = comms->util->gameInfo->gameID; gameID = comms->util->gameInfo->gameID;
} }
// XP_ASSERT( 0 != gameID );
if ( 0 == gameID ) {
XP_LOGF( "%s: gameID STILL 0", __func__ );
} else if ( 0 == comms->util->gameInfo->gameID ) {
XP_LOGF( "%s: setting gi's gameID to 0X%lX", __func__, gameID );
comms->util->gameInfo->gameID = gameID;
}
/* Most of the time these will be the same, but early in a game they won't /* Most of the time these will be the same, but early in a game they won't
be. Would be nice not to have to use gameID. */ be. Would be nice not to have to use gameID. */
if ( 0 == gameID ) { if ( 0 == gameID ) {

View file

@ -198,6 +198,7 @@ CommsCtxt* comms_makeFromStream( MPFORMAL XWStreamCtxt* stream,
XW_UtilCtxt* util, XW_UtilCtxt* util,
const TransportProcs* procs ); const TransportProcs* procs );
void comms_start( CommsCtxt* comms ); void comms_start( CommsCtxt* comms );
void comms_stop( CommsCtxt* comms );
void comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream, void comms_writeToStream( CommsCtxt* comms, XWStreamCtxt* stream,
XP_U16 saveToken ); XP_U16 saveToken );
void comms_saveSucceeded( CommsCtxt* comms, XP_U16 saveToken ); void comms_saveSucceeded( CommsCtxt* comms, XP_U16 saveToken );

View file

@ -249,7 +249,7 @@ typedef struct _PlayerDicts {
# define RELAY_ROOM_DEFAULT "Room 1" # define RELAY_ROOM_DEFAULT "Room 1"
#endif #endif
#ifndef RELAY_PORT_DEFAULT #ifndef RELAY_PORT_DEFAULT
# define RELAY_PORT_DEFAULT 10999 # define RELAY_PORT_DEFAULT 10997
#endif #endif
#ifdef MEM_DEBUG #ifdef MEM_DEBUG

View file

@ -133,62 +133,67 @@ game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
board_prefsChanged( game->board, cp ); board_prefsChanged( game->board, cp );
} /* game_makeNewGame */ } /* game_makeNewGame */
void XP_Bool
game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, game_reset( MPFORMAL XWGame* game, CurGameInfo* gi,
XW_UtilCtxt* XP_UNUSED_STANDALONE(util), XW_UtilCtxt* XP_UNUSED_STANDALONE(util),
CommonPrefs* cp, const TransportProcs* procs ) CommonPrefs* cp, const TransportProcs* procs )
{ {
XP_Bool result = XP_FALSE;
XP_U16 ii; XP_U16 ii;
XP_ASSERT( !!game->model ); if ( !!game->model ) {
XP_ASSERT( !!gi ); XP_ASSERT( !!game->model );
XP_ASSERT( !!gi );
gi->gameID = makeGameID( util ); gi->gameID = makeGameID( util );
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
XP_U16 nPlayersHere = 0; XP_U16 nPlayersHere = 0;
XP_U16 nPlayersTotal = 0; XP_U16 nPlayersTotal = 0;
checkServerRole( gi, &nPlayersHere, &nPlayersTotal ); checkServerRole( gi, &nPlayersHere, &nPlayersTotal );
if ( !!game->comms ) { if ( !!game->comms ) {
if ( gi->serverRole == SERVER_STANDALONE ) { if ( gi->serverRole == SERVER_STANDALONE ) {
comms_destroy( game->comms ); comms_destroy( game->comms );
game->comms = NULL; game->comms = NULL;
} else { } else {
comms_reset( game->comms, gi->serverRole != SERVER_ISCLIENT, comms_reset( game->comms, gi->serverRole != SERVER_ISCLIENT,
nPlayersHere, nPlayersTotal ); nPlayersHere, nPlayersTotal );
} }
} else if ( gi->serverRole != SERVER_STANDALONE ) { } else if ( gi->serverRole != SERVER_STANDALONE ) {
game->comms = comms_make( MPPARM(mpool) util, game->comms = comms_make( MPPARM(mpool) util,
gi->serverRole != SERVER_ISCLIENT, gi->serverRole != SERVER_ISCLIENT,
nPlayersHere, nPlayersTotal, procs nPlayersHere, nPlayersTotal, procs
#ifdef SET_GAMESEED #ifdef SET_GAMESEED
, 0 , 0
#endif #endif
); );
} }
#else #else
# ifdef DEBUG # ifdef DEBUG
mpool = mpool; /* quash unused formal warning */ mpool = mpool; /* quash unused formal warning */
# endif # endif
#endif #endif
model_setSize( game->model, gi->boardSize ); model_setSize( game->model, gi->boardSize );
server_reset( game->server, server_reset( game->server,
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
game->comms game->comms
#else #else
NULL NULL
#endif #endif
); );
board_reset( game->board ); board_reset( game->board );
for ( ii = 0; ii < gi->nPlayers; ++ii ) { for ( ii = 0; ii < gi->nPlayers; ++ii ) {
gi->players[ii].secondsUsed = 0; gi->players[ii].secondsUsed = 0;
}
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
result = XP_TRUE;
} }
return result;
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
} /* game_reset */ } /* game_reset */
#ifdef XWFEATURE_CHANGEDICT #ifdef XWFEATURE_CHANGEDICT
@ -335,6 +340,7 @@ game_dispose( XWGame* game )
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
if ( !!game->comms ) { if ( !!game->comms ) {
comms_stop( game->comms );
comms_destroy( game->comms ); comms_destroy( game->comms );
game->comms = NULL; game->comms = NULL;
} }

View file

@ -93,8 +93,8 @@ void game_makeNewGame( MPFORMAL XWGame* game, CurGameInfo* gi,
,XP_U16 gameSeed ,XP_U16 gameSeed
#endif #endif
); );
void game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XW_UtilCtxt* util, XP_Bool game_reset( MPFORMAL XWGame* game, CurGameInfo* gi, XW_UtilCtxt* util,
CommonPrefs* cp, const TransportProcs* procs ); CommonPrefs* cp, const TransportProcs* procs );
void game_changeDict( MPFORMAL XWGame* game, CurGameInfo* gi, void game_changeDict( MPFORMAL XWGame* game, CurGameInfo* gi,
DictionaryCtxt* dict ); DictionaryCtxt* dict );

View file

@ -78,19 +78,21 @@ checkIsText( MemPoolEntry* entry )
{ {
unsigned char* txt = (unsigned char*)entry->ptr; unsigned char* txt = (unsigned char*)entry->ptr;
XP_U32 len = entry->size; XP_U32 len = entry->size;
char* result = NULL;
while ( len-- ) { if ( 0 < len ) {
unsigned char c = *txt++; while ( len-- ) {
if ( c < 32 || c > 127 ) { unsigned char c = *txt++;
if ( len == 0 && c == '\0' ) { if ( c < 32 || c > 127 ) {
return (char*)entry->ptr; if ( len == 0 && c == '\0' ) {
} else { result = (char*)entry->ptr;
return (char*)NULL; }
break;
} }
} }
} }
return (char*)NULL; return result;
} /* checkIsText */ } /* checkIsText */
#endif #endif
@ -226,6 +228,7 @@ mpool_free( MemPoolCtx* mpool, void* ptr, const char* file,
if ( !entry ) { if ( !entry ) {
XP_LOGF( "findEntryFor failed; called from %s, line %ld in %s", XP_LOGF( "findEntryFor failed; called from %s, line %ld in %s",
func, lineNo, file ); func, lineNo, file );
XP_ASSERT( 0 );
} else { } else {
#ifdef MPOOL_DEBUG #ifdef MPOOL_DEBUG
@ -249,11 +252,7 @@ mpool_free( MemPoolCtx* mpool, void* ptr, const char* file,
++mpool->nFree; ++mpool->nFree;
--mpool->nUsed; --mpool->nUsed;
return;
} }
XP_ASSERT( 0 );
} /* mpool_free */ } /* mpool_free */
void void

View file

@ -6,3 +6,4 @@ obj_linux_rel
log_*_*.txt log_*_*.txt
discon_ok2.sh_logs discon_ok2.sh_logs
test_backsend.sh_* test_backsend.sh_*
games.db

View file

@ -101,6 +101,7 @@ DEFINES += -DXWFEATURE_WALKDICT
DEFINES += -DXWFEATURE_WALKDICT_FILTER DEFINES += -DXWFEATURE_WALKDICT_FILTER
DEFINES += -DXWFEATURE_DICTSANITY DEFINES += -DXWFEATURE_DICTSANITY
DEFINES += -DHASH_STREAM DEFINES += -DHASH_STREAM
DEFINES += -DRELAY_NAME_DEFAULT="\"localhost\""
#DEFINES += -DXWFEATURE_SCOREONEPASS #DEFINES += -DXWFEATURE_SCOREONEPASS
### Enable zero or one of these two ### ### Enable zero or one of these two ###
#DEFINES += -DXWFEATURE_TRAYUNDO_ALL #DEFINES += -DXWFEATURE_TRAYUNDO_ALL
@ -112,6 +113,7 @@ DEFINES += -DXWFEATURE_HILITECELL
DEFINES += -DXWFEATURE_CHANGEDICT DEFINES += -DXWFEATURE_CHANGEDICT
DEFINES += -DXWFEATURE_DEVID DEFINES += -DXWFEATURE_DEVID
DEFINES += -DXWFEATURE_COMMSACK DEFINES += -DXWFEATURE_COMMSACK
DEFINES += -DCOMMS_XPORT_FLAGSPROC
# MAX_ROWS controls STREAM_VERS_BIGBOARD and with it move hashing # MAX_ROWS controls STREAM_VERS_BIGBOARD and with it move hashing
DEFINES += -DMAX_ROWS=32 DEFINES += -DMAX_ROWS=32
@ -169,6 +171,7 @@ INCLUDES += ${EXTRAINCS}
ifdef DO_GTK ifdef DO_GTK
GTK_OBJS = \ GTK_OBJS = \
$(BUILD_PLAT_DIR)/gtkmain.o \ $(BUILD_PLAT_DIR)/gtkmain.o \
$(BUILD_PLAT_DIR)/gtkboard.o \
$(BUILD_PLAT_DIR)/gtkdraw.o \ $(BUILD_PLAT_DIR)/gtkdraw.o \
$(BUILD_PLAT_DIR)/gtkask.o \ $(BUILD_PLAT_DIR)/gtkask.o \
$(BUILD_PLAT_DIR)/gtkletterask.o \ $(BUILD_PLAT_DIR)/gtkletterask.o \
@ -201,6 +204,8 @@ OBJ = \
$(BUILD_PLAT_DIR)/linuxsms.o \ $(BUILD_PLAT_DIR)/linuxsms.o \
$(BUILD_PLAT_DIR)/linuxdict.o \ $(BUILD_PLAT_DIR)/linuxdict.o \
$(BUILD_PLAT_DIR)/linuxutl.o \ $(BUILD_PLAT_DIR)/linuxutl.o \
$(BUILD_PLAT_DIR)/gamesdb.o \
$(BUILD_PLAT_DIR)/relaycon.o \
$(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS) $(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS)
LIBS = -lm -luuid $(GPROFFLAG) LIBS = -lm -luuid $(GPROFFLAG)

View file

@ -32,8 +32,8 @@
/* Figure out how many lines there are and how wide the widest is. /* Figure out how many lines there are and how wide the widest is.
*/ */
short short
cursesask( CursesAppGlobals* globals, char* question, short numButtons, cursesask( CursesAppGlobals* globals, const char* question, short numButtons,
char* button1, ... ) const char* button1, ... )
{ {
WINDOW* confWin; WINDOW* confWin;
int x, y, rows, row, nLines; int x, y, rows, row, nLines;

View file

@ -22,8 +22,8 @@
#include "cursesmain.h" #include "cursesmain.h"
short cursesask( CursesAppGlobals* globals, char* question, short cursesask( CursesAppGlobals* globals, const char* question,
short numButtons, char* button1, ... ); short numButtons, const char* button1, ... );
#endif #endif

View file

@ -70,7 +70,7 @@ measureAskText( const XP_UCHAR* question, int width, FormatInfo* fip )
void void
drawButtons( WINDOW* win, XP_U16 line, short spacePerButton, drawButtons( WINDOW* win, XP_U16 line, short spacePerButton,
short numButtons, short curSelButton, char** button1 ) short numButtons, short curSelButton, const char** button1 )
{ {
short i; short i;
for ( i = 0; i < numButtons; ++i ) { for ( i = 0; i < numButtons; ++i ) {

View file

@ -44,6 +44,6 @@ typedef struct FormatInfo {
void measureAskText( const XP_UCHAR* question, int maxWidth, FormatInfo* fip ); void measureAskText( const XP_UCHAR* question, int maxWidth, FormatInfo* fip );
void drawButtons( WINDOW* confWin, XP_U16 line, short spacePerButton, void drawButtons( WINDOW* confWin, XP_U16 line, short spacePerButton,
short numButtons, short curSelButton, char** button1 ); short numButtons, short curSelButton, const char** button1 );
#endif #endif

View file

@ -57,7 +57,7 @@ curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query,
short curSelButton = 1; /* force draw by being different */ short curSelButton = 1; /* force draw by being different */
short maxWidth; short maxWidth;
short numCtlButtons; short numCtlButtons;
char* ctlButtons[] = { "Ok", "Cancel" }; const char* ctlButtons[] = { "Ok", "Cancel" };
XP_Bool dismissed = XP_FALSE; XP_Bool dismissed = XP_FALSE;
FormatInfo fi; FormatInfo fi;
int len; int len;
@ -138,7 +138,7 @@ curses_askLetter( CursesAppGlobals* globals, XP_UCHAR* query,
MAX_TILE_BUTTON_WIDTH-1, MAX_TILE_BUTTON_WIDTH-1,
nInRow, nInRow,
newSelButton - textsOffsets[i], newSelButton - textsOffsets[i],
(char**)&textPtrs[textsOffsets[i]] ); (const char**)&textPtrs[textsOffsets[i]] );
} }

View file

@ -42,6 +42,7 @@
#include "linuxmain.h" #include "linuxmain.h"
#include "linuxutl.h" #include "linuxutl.h"
#include "linuxdict.h"
#include "cursesmain.h" #include "cursesmain.h"
#include "cursesask.h" #include "cursesask.h"
#include "cursesletterask.h" #include "cursesletterask.h"
@ -61,6 +62,8 @@
#include "dbgutil.h" #include "dbgutil.h"
#include "linuxsms.h" #include "linuxsms.h"
#include "linuxudp.h" #include "linuxudp.h"
#include "gamesdb.h"
#include "relaycon.h"
#ifdef CURSES_SMALL_SCREEN #ifdef CURSES_SMALL_SCREEN
# define MENU_WINDOW_HEIGHT 1 # define MENU_WINDOW_HEIGHT 1
@ -208,7 +211,7 @@ static CursesMenuHandler getHandlerForKey( const MenuList* list, char ch );
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
# define MEMPOOL params->util->mpool, # define MEMPOOL cGlobals->util->mpool,
#else #else
# define MEMPOOL # define MEMPOOL
#endif #endif
@ -237,7 +240,7 @@ curses_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128]; char query[128];
XP_S16 index; XP_S16 index;
char* playerName = globals->cGlobals.params->gi.players[playerNum].name; char* playerName = globals->cGlobals.gi->players[playerNum].name;
snprintf( query, sizeof(query), snprintf( query, sizeof(query),
"Pick tile for %s! (Tab or type letter to select " "Pick tile for %s! (Tab or type letter to select "
@ -255,7 +258,7 @@ curses_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi),
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128]; char query[128];
XP_S16 index; XP_S16 index;
char* playerName = globals->cGlobals.params->gi.players[playerNum].name; char* playerName = globals->cGlobals.gi->players[playerNum].name;
snprintf( query, sizeof(query), snprintf( query, sizeof(query),
"Pick tile for %s! (Tab or type letter to select " "Pick tile for %s! (Tab or type letter to select "
@ -343,7 +346,7 @@ cursesShowFinalScores( CursesAppGlobals* globals )
XWStreamCtxt* stream; XWStreamCtxt* stream;
XP_UCHAR* text; XP_UCHAR* text;
stream = mem_stream_make( MPPARM(globals->cGlobals.params->util->mpool) stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
globals->cGlobals.params->vtMgr, globals->cGlobals.params->vtMgr,
globals, CHANNEL_NONE, NULL ); globals, CHANNEL_NONE, NULL );
server_writeFinalScores( globals->cGlobals.game.server, stream ); server_writeFinalScores( globals->cGlobals.game.server, stream );
@ -408,6 +411,14 @@ curses_util_informNetDict( XW_UtilCtxt* uc, XP_LangCode XP_UNUSED(lang),
XP_LOGF( "%s: %s => %s (cksum: %s)", __func__, oldName, newName, newSum ); XP_LOGF( "%s: %s => %s (cksum: %s)", __func__, oldName, newName, newSum );
} }
static void
curses_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
{
LOG_FUNC();
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
linuxSetIsServer( cGlobals, isServer );
}
#ifdef XWFEATURE_HILITECELL #ifdef XWFEATURE_HILITECELL
static XP_Bool static XP_Bool
curses_util_hiliteCell( XW_UtilCtxt* uc, curses_util_hiliteCell( XW_UtilCtxt* uc,
@ -1039,7 +1050,7 @@ data_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data )
if ( 0 != (G_IO_IN & condition) ) { if ( 0 != (G_IO_IN & condition) ) {
CursesAppGlobals* globals = (CursesAppGlobals*)data; CursesAppGlobals* globals = (CursesAppGlobals*)data;
int fd = g_io_channel_unix_get_fd( source ); int fd = g_io_channel_unix_get_fd( source );
unsigned char buf[256]; unsigned char buf[1024];
int nBytes; int nBytes;
CommsAddrRec addrRec; CommsAddrRec addrRec;
CommsAddrRec* addrp = NULL; CommsAddrRec* addrp = NULL;
@ -1087,7 +1098,7 @@ data_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data )
that on the screen before giving the server another that on the screen before giving the server another
shot. So just call the idle proc. */ shot. So just call the idle proc. */
if ( redraw ) { if ( redraw ) {
curses_util_requestTime( globals->cGlobals.params->util ); curses_util_requestTime( globals->cGlobals.util );
} }
} }
} }
@ -1116,6 +1127,15 @@ curses_socket_changed( void* closure, int oldSock, int newSock,
#endif #endif
} /* curses_socket_changed */ } /* curses_socket_changed */
static void
curses_onGameSaved( void* closure, sqlite3_int64 rowid,
XP_Bool XP_UNUSED(firstTime) )
{
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
/* May not be recorded */
globals->cGlobals.selRow = rowid;
}
#ifdef USE_GLIBLOOP #ifdef USE_GLIBLOOP
static gboolean static gboolean
handle_quitwrite( GIOChannel* XP_UNUSED(source), GIOCondition XP_UNUSED(condition), gpointer data ) handle_quitwrite( GIOChannel* XP_UNUSED(source), GIOCondition XP_UNUSED(condition), gpointer data )
@ -1284,7 +1304,7 @@ blocking_gotEvent( CursesAppGlobals* globals, int* ch )
globals ); globals );
} else { } else {
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
unsigned char buf[256]; unsigned char buf[1024];
int nBytes; int nBytes;
CommsAddrRec addrRec; CommsAddrRec addrRec;
CommsAddrRec* addrp = NULL; CommsAddrRec* addrp = NULL;
@ -1489,7 +1509,7 @@ curses_util_remSelected( XW_UtilCtxt* uc )
XWStreamCtxt* stream; XWStreamCtxt* stream;
XP_UCHAR* text; XP_UCHAR* text;
stream = mem_stream_make( MPPARM(globals->cGlobals.params->util->mpool) stream = mem_stream_make( MPPARM(globals->cGlobals.util->mpool)
globals->cGlobals.params->vtMgr, globals->cGlobals.params->vtMgr,
globals, CHANNEL_NONE, NULL ); globals, CHANNEL_NONE, NULL );
board_formatRemainingTiles( globals->cGlobals.game.board, stream ); board_formatRemainingTiles( globals->cGlobals.game.board, stream );
@ -1553,6 +1573,8 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_informUndo = curses_util_informUndo; util->vtable->m_util_informUndo = curses_util_informUndo;
util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver; util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver;
util->vtable->m_util_informNetDict = curses_util_informNetDict; util->vtable->m_util_informNetDict = curses_util_informNetDict;
util->vtable->m_util_setIsServer = curses_util_setIsServer;
#ifdef XWFEATURE_HILITECELL #ifdef XWFEATURE_HILITECELL
util->vtable->m_util_hiliteCell = curses_util_hiliteCell; util->vtable->m_util_hiliteCell = curses_util_hiliteCell;
#endif #endif
@ -1609,7 +1631,7 @@ positionSizeStuff( CursesAppGlobals* globals, int width, int height )
XP_U16 cellWidth, cellHt, scoreLeft, scoreWidth; XP_U16 cellWidth, cellHt, scoreLeft, scoreWidth;
BoardCtxt* board = globals->cGlobals.game.board; BoardCtxt* board = globals->cGlobals.game.board;
int remWidth = width; int remWidth = width;
int nRows = globals->cGlobals.params->gi.boardSize; int nRows = globals->cGlobals.gi->boardSize;
cellWidth = CURSES_CELL_WIDTH; cellWidth = CURSES_CELL_WIDTH;
cellHt = CURSES_CELL_HT; cellHt = CURSES_CELL_HT;
@ -1719,6 +1741,119 @@ handle_stdin( GIOChannel* XP_UNUSED_DBG(source), GIOCondition condition,
} }
#endif #endif
#ifdef COMMS_XPORT_FLAGSPROC
static XP_U32
curses_getFlags( void* XP_UNUSED(closure) )
{
return COMMS_XPORT_FLAGS_HASNOCONN;
}
#endif
static void
cursesGotBuf( void* closure, const XP_U8* buf, XP_U16 len )
{
LOG_FUNC();
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
XP_U32 gameToken;
XP_ASSERT( sizeof(gameToken) < len );
gameToken = ntohl(*(XP_U32*)&buf[0]);
buf += sizeof(gameToken);
len -= sizeof(gameToken);
gameGotBuf( &globals->cGlobals, XP_TRUE, buf, len );
}
static gint
curses_requestMsgs( gpointer data )
{
CursesAppGlobals* globals = (CursesAppGlobals*)data;
XP_UCHAR devIDBuf[64] = {0};
db_fetch( globals->cGlobals.pDb, KEY_RDEVID, devIDBuf, sizeof(devIDBuf) );
if ( '\0' != devIDBuf[0] ) {
relaycon_requestMsgs( globals->cGlobals.params, devIDBuf );
} else {
XP_LOGF( "%s: not requesting messages as don't have relay id", __func__ );
}
return 0; /* don't run again */
}
static void
cursesNoticeRcvd( void* closure )
{
LOG_FUNC();
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
(void)g_idle_add( curses_requestMsgs, globals );
}
static void
cursesDevIDChanged( void* closure, const XP_UCHAR* devID )
{
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
CommonGlobals* cGlobals = &globals->cGlobals;
sqlite3* pDb = cGlobals->pDb;
if ( !!devID ) {
XP_LOGF( "%s(devID=%s)", __func__, devID );
db_store( pDb, KEY_RDEVID, devID );
} else {
XP_LOGF( "%s: bad relayid", __func__ );
db_remove( pDb, KEY_RDEVID );
DevIDType typ;
const XP_UCHAR* devID = linux_getDevID( cGlobals->params, &typ );
relaycon_reg( cGlobals->params, devID, typ );
}
}
static void
cursesErrorMsgRcvd( void* closure, const XP_UCHAR* msg )
{
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
if ( !!globals->lastErr && 0 == strcmp( globals->lastErr, msg ) ) {
XP_LOGF( "skipping error message from relay" );
} else {
g_free( globals->lastErr );
globals->lastErr = g_strdup( msg );
(void)cursesask( globals, msg, 1, "Ok" );
}
}
static gboolean
curses_app_socket_proc( GIOChannel* source, GIOCondition condition,
gpointer data )
{
if ( 0 != (G_IO_IN & condition) ) {
CursesAppGlobals* globals = (CursesAppGlobals*)data;
int socket = g_io_channel_unix_get_fd( source );
GList* iter;
for ( iter = globals->sources; !!iter; iter = iter->next ) {
SourceData* sd = (SourceData*)iter->data;
if ( sd->channel == source ) {
(*sd->proc)( sd->procClosure, socket );
break;
}
}
XP_ASSERT( !!iter ); /* didn't fail to find it */
}
return TRUE;
}
static void
cursesUDPSocketChanged( void* closure, int newSock, int XP_UNUSED(oldSock),
SockReceiver proc, void* procClosure )
{
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
SourceData* sd = g_malloc( sizeof(*sd) );
sd->channel = g_io_channel_unix_new( newSock );
sd->watch = g_io_add_watch( sd->channel, G_IO_IN | G_IO_ERR,
curses_app_socket_proc, globals );
sd->proc = proc;
sd->procClosure = procClosure;
globals->sources = g_list_append( globals->sources, sd );
}
static gboolean static gboolean
chatsTimerFired( gpointer data ) chatsTimerFired( gpointer data )
{ {
@ -1748,6 +1883,7 @@ void
cursesmain( XP_Bool isServer, LaunchParams* params ) cursesmain( XP_Bool isServer, LaunchParams* params )
{ {
int width, height; int width, height;
CommonGlobals* cGlobals = &g_globals.cGlobals;
memset( &g_globals, 0, sizeof(g_globals) ); memset( &g_globals, 0, sizeof(g_globals) );
@ -1763,6 +1899,9 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
g_globals.cGlobals.socketChanged = curses_socket_changed; g_globals.cGlobals.socketChanged = curses_socket_changed;
g_globals.cGlobals.socketChangedClosure = &g_globals; g_globals.cGlobals.socketChangedClosure = &g_globals;
g_globals.cGlobals.onSave = curses_onGameSaved;
g_globals.cGlobals.onSaveClosure = &g_globals;
g_globals.cGlobals.addAcceptor = curses_socket_acceptor; g_globals.cGlobals.addAcceptor = curses_socket_acceptor;
g_globals.cGlobals.cp.showBoardArrow = XP_TRUE; g_globals.cGlobals.cp.showBoardArrow = XP_TRUE;
@ -1778,7 +1917,11 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
g_globals.cGlobals.cp.robotTradePct = params->robotTradePct; g_globals.cGlobals.cp.robotTradePct = params->robotTradePct;
#endif #endif
setupCursesUtilCallbacks( &g_globals, params->util ); g_globals.cGlobals.gi = &params->pgi;
setupUtil( &g_globals.cGlobals );
setupCursesUtilCallbacks( &g_globals, g_globals.cGlobals.util );
initFromParams( &g_globals.cGlobals, params );
#ifdef XWFEATURE_RELAY #ifdef XWFEATURE_RELAY
if ( params->conType == COMMS_CONN_RELAY ) { if ( params->conType == COMMS_CONN_RELAY ) {
@ -1792,12 +1935,14 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
cursesListenOnSocket( &g_globals, 0, handle_stdin ); cursesListenOnSocket( &g_globals, 0, handle_stdin );
} }
setOneSecondTimer( &g_globals.cGlobals ); setOneSecondTimer( &g_globals.cGlobals );
# ifdef DEBUG # ifdef DEBUG
int piperesult = int piperesult =
# endif # endif
pipe( g_globals.quitpipe ); pipe( g_globals.quitpipe );
XP_ASSERT( piperesult == 0 ); XP_ASSERT( piperesult == 0 );
cursesListenOnSocket( &g_globals, g_globals.quitpipe[0], handle_quitwrite ); cursesListenOnSocket( &g_globals, g_globals.quitpipe[0], handle_quitwrite );
#else #else
cursesListenOnSocket( &g_globals, 0 ); /* stdin */ cursesListenOnSocket( &g_globals, 0 ); /* stdin */
@ -1824,7 +1969,9 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
.rconnd = relay_connd_curses, .rconnd = relay_connd_curses,
.rerror = relay_error_curses, .rerror = relay_error_curses,
.sendNoConn = relay_sendNoConn_curses, .sendNoConn = relay_sendNoConn_curses,
.flags = COMMS_XPORT_FLAGS_HASNOCONN, # ifdef COMMS_XPORT_FLAGSPROC
.getFlags = curses_getFlags,
# endif
#endif #endif
}; };
@ -1846,7 +1993,40 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
cursesDrawCtxtMake( g_globals.boardWin ); cursesDrawCtxtMake( g_globals.boardWin );
XWStreamCtxt* stream = NULL; XWStreamCtxt* stream = NULL;
if ( !!params->fileName && file_exists( params->fileName ) ) { if ( !!params->dbName ) {
g_globals.cGlobals.pDb = openGamesDB( params->dbName );
RelayConnProcs procs = {
.msgReceived = cursesGotBuf,
.msgNoticeReceived = cursesNoticeRcvd,
.devIDChanged = cursesDevIDChanged,
.msgErrorMsg = cursesErrorMsgRcvd,
.socketChanged = cursesUDPSocketChanged,
};
relaycon_init( params, &procs, &g_globals,
params->connInfo.relay.relayName,
params->connInfo.relay.defaultSendPort );
DevIDType typ;
const XP_UCHAR* devID = linux_getDevID( params, &typ );
relaycon_reg( params, devID, typ );
GSList* games = listGames( g_globals.cGlobals.pDb );
if ( !!games ) {
stream = mem_stream_make( MEMPOOL params->vtMgr,
&g_globals.cGlobals, CHANNEL_NONE,
NULL );
sqlite3_int64 selRow = *(sqlite3_int64*)games->data;
if ( loadGame( stream, g_globals.cGlobals.pDb, selRow ) ) {
g_globals.cGlobals.selRow = selRow;
} else {
stream_destroy( stream );
stream = NULL;
}
g_slist_free( games );
}
} else if ( !!params->fileName && file_exists( params->fileName ) ) {
stream = streamFromFile( &g_globals.cGlobals, params->fileName, stream = streamFromFile( &g_globals.cGlobals, params->fileName,
&g_globals ); &g_globals );
#ifdef USE_SQLITE #ifdef USE_SQLITE
@ -1855,27 +2035,40 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
#endif #endif
} }
if ( NULL == cGlobals->dict ) {
if ( !!stream ) {
cGlobals->dict = makeDictForStream( cGlobals, stream );
} else {
cGlobals->dict =
linux_dictionary_make( MEMPOOL params,
cGlobals->gi->dictName, XP_TRUE );
}
}
cGlobals->gi->dictLang = dict_getLangCode( cGlobals->dict );
if ( !!stream ) { if ( !!stream ) {
(void)game_makeFromStream( MEMPOOL stream, &g_globals.cGlobals.game, (void)game_makeFromStream( MEMPOOL stream, &cGlobals->game,
&params->gi, params->dict, &params->dicts, cGlobals->gi, cGlobals->dict,
params->util, &cGlobals->dicts, cGlobals->util,
(DrawCtx*)g_globals.draw, (DrawCtx*)g_globals.draw,
&g_globals.cGlobals.cp, &procs ); &g_globals.cGlobals.cp, &procs );
stream_destroy( stream ); stream_destroy( stream );
if ( !isServer && params->gi.serverRole == SERVER_ISSERVER ) { if ( !isServer && cGlobals->gi->serverRole == SERVER_ISSERVER ) {
isServer = XP_TRUE; isServer = XP_TRUE;
} }
opened = XP_TRUE; opened = XP_TRUE;
} }
if ( !opened ) { if ( !opened ) {
game_makeNewGame( MEMPOOL &g_globals.cGlobals.game, &params->gi, game_makeNewGame( MEMPOOL &cGlobals->game, cGlobals->gi,
params->util, (DrawCtx*)g_globals.draw, cGlobals->util, (DrawCtx*)g_globals.draw,
&g_globals.cGlobals.cp, &procs, params->gameSeed ); &g_globals.cGlobals.cp, &procs, params->gameSeed );
g_globals.cGlobals.selRow = -1;
saveGame( &g_globals.cGlobals );
} }
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
if ( g_globals.cGlobals.game.comms ) { if ( cGlobals->game.comms ) {
CommsAddrRec addr = {0}; CommsAddrRec addr = {0};
if ( 0 ) { if ( 0 ) {
@ -1907,22 +2100,22 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
sizeof(params->connInfo.bt.hostAddr) ); sizeof(params->connInfo.bt.hostAddr) );
# endif # endif
} }
comms_setAddr( g_globals.cGlobals.game.comms, &addr ); comms_setAddr( cGlobals->game.comms, &addr );
} }
#endif #endif
model_setDictionary( g_globals.cGlobals.game.model, params->dict ); model_setDictionary( cGlobals->game.model, cGlobals->dict );
setSquareBonuses( &g_globals.cGlobals ); setSquareBonuses( cGlobals );
positionSizeStuff( &g_globals, width, height ); positionSizeStuff( &g_globals, width, height );
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
/* send any events that need to get off before the event loop begins */ /* send any events that need to get off before the event loop begins */
if ( !isServer ) { if ( !isServer ) {
if ( 1 /* stream_open( params->info.clientInfo.stream ) */) { if ( 1 /* stream_open( params->info.clientInfo.stream ) */) {
server_initClientConnection( g_globals.cGlobals.game.server, server_initClientConnection( cGlobals->game.server,
mem_stream_make( MEMPOOL mem_stream_make( MEMPOOL
params->vtMgr, params->vtMgr,
&g_globals.cGlobals, cGlobals,
(XP_PlayerAddr)0, (XP_PlayerAddr)0,
sendOnClose ) ); sendOnClose ) );
} else { } else {
@ -1961,10 +2154,13 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
} }
#endif #endif
} }
if ( !!g_globals.cGlobals.game.comms ) {
comms_stop( g_globals.cGlobals.game.comms );
}
saveGame( &g_globals.cGlobals ); saveGame( &g_globals.cGlobals );
game_dispose( &g_globals.cGlobals.game ); /* takes care of the dict */ game_dispose( &g_globals.cGlobals.game ); /* takes care of the dict */
gi_disposePlayerInfo( MEMPOOL &g_globals.cGlobals.params->gi ); gi_disposePlayerInfo( MEMPOOL cGlobals->gi );
#ifdef XWFEATURE_BLUETOOTH #ifdef XWFEATURE_BLUETOOTH
linux_bt_close( &g_globals.cGlobals ); linux_bt_close( &g_globals.cGlobals );
@ -1977,5 +2173,12 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
#endif #endif
endwin(); endwin();
if ( !!params->dbName ) {
closeGamesDB( g_globals.cGlobals.pDb );
}
relaycon_cleanup( params );
linux_util_vt_destroy( g_globals.cGlobals.util );
} /* cursesmain */ } /* cursesmain */
#endif /* PLATFORM_NCURSES */ #endif /* PLATFORM_NCURSES */

View file

@ -68,6 +68,7 @@ struct CursesAppGlobals {
XP_Bool doDraw; XP_Bool doDraw;
const struct MenuList* menuList; const struct MenuList* menuList;
XP_U16 nLinesMenu; XP_U16 nLinesMenu;
gchar* lastErr;
XP_U16 nChatsSent; XP_U16 nChatsSent;
@ -98,13 +99,6 @@ struct CursesAppGlobals {
#endif #endif
}; };
#ifdef USE_GLIBLOOP
typedef struct _SourceData {
GIOChannel* channel;
gint watch;
} SourceData;
#endif
DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin ); DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );
/* Ports: Client and server pick a port at startup on which they'll listen. /* Ports: Client and server pick a port at startup on which they'll listen.

292
xwords4/linux/gamesdb.c Normal file
View file

@ -0,0 +1,292 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000-2013 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 "gamesdb.h"
#include "main.h"
static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
int len );
sqlite3*
openGamesDB( const char* dbName )
{
sqlite3* pDb = NULL;
int result = sqlite3_open( dbName, &pDb );
XP_ASSERT( SQLITE_OK == result );
const char* createGamesStr =
"CREATE TABLE games ( "
"rowid INTEGER PRIMARY KEY AUTOINCREMENT"
",game BLOB"
",room VARCHAR(32)"
",ended INT(1)"
",turn INT(2)"
",nmoves INT"
",nmissing INT(2)"
")";
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
const char* createValuesStr =
"CREATE TABLE pairs ( key TEXT UNIQUE,value TEXT )";
result = sqlite3_exec( pDb, createValuesStr, NULL, NULL, NULL );
XP_LOGF( "sqlite3_exec=>%d", result );
return pDb;
}
void
closeGamesDB( sqlite3* pDb )
{
sqlite3_close( pDb );
XP_LOGF( "%s finished", __func__ );
}
void
writeToDB( XWStreamCtxt* stream, void* closure )
{
int result;
CommonGlobals* cGlobals = (CommonGlobals*)closure;
sqlite3_int64 selRow = cGlobals->selRow;
sqlite3* pDb = cGlobals->pDb;
XP_U16 len = stream_getSize( stream );
char buf[256];
char* query;
sqlite3_stmt* stmt = NULL;
XP_Bool newGame = -1 == selRow;
if ( newGame ) { /* new row; need to insert blob first */
query = "INSERT INTO games (game) VALUES (?)";
} else {
const char* fmt = "UPDATE games SET game=? where rowid=%lld";
snprintf( buf, sizeof(buf), fmt, selRow );
query = buf;
}
result = sqlite3_prepare_v2( pDb, query, -1, &stmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_bind_zeroblob( stmt, 1 /*col 0 ??*/, len );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( stmt );
XP_ASSERT( SQLITE_DONE == result );
if ( newGame ) { /* new row; need to insert blob first */
selRow = sqlite3_last_insert_rowid( pDb );
XP_LOGF( "%s: new rowid: %lld", __func__, selRow );
cGlobals->selRow = selRow;
}
sqlite3_blob* blob;
result = sqlite3_blob_open( pDb, "main", "games", "game",
selRow, 1 /*flags: writeable*/, &blob );
XP_ASSERT( SQLITE_OK == result );
const XP_U8* ptr = stream_getPtr( stream );
result = sqlite3_blob_write( blob, ptr, len, 0 );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_blob_close( blob );
XP_ASSERT( SQLITE_OK == result );
if ( !!stmt ) {
sqlite3_finalize( stmt );
}
(*cGlobals->onSave)( cGlobals->onSaveClosure, selRow, newGame );
}
void
summarize( CommonGlobals* cGlobals )
{
XP_S16 nMoves = model_getNMoves( cGlobals->game.model );
XP_Bool gameOver = server_getGameIsOver( cGlobals->game.server );
XP_S16 turn = server_getCurrentTurn( cGlobals->game.server );
XP_S16 nMissing = 0;
CommsAddrRec addr = {0};
gchar* room = "";
if ( !!cGlobals->game.comms ) {
nMissing = server_getMissingPlayers( cGlobals->game.server );
comms_getAddr( cGlobals->game.comms, &addr );
if ( COMMS_CONN_RELAY == addr.conType ) {
room = addr.u.ip_relay.invite;
}
}
const char* fmt = "UPDATE games SET room='%s', ended=%d, turn=%d, nmissing=%d, nmoves=%d"
" where rowid=%lld";
XP_UCHAR buf[256];
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, nMissing, nMoves,
cGlobals->selRow );
XP_LOGF( "query: %s", buf );
sqlite3_stmt* stmt = NULL;
int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( stmt );
XP_ASSERT( SQLITE_DONE == result );
sqlite3_finalize( stmt );
}
GSList*
listGames( sqlite3* pDb )
{
GSList* list = NULL;
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb,
"SELECT rowid FROM games ORDER BY rowid",
-1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
while ( NULL != ppStmt ) {
switch( sqlite3_step( ppStmt ) ) {
case SQLITE_ROW: /* have data */
{
sqlite3_int64* data = g_malloc( sizeof( *data ) );
*data = sqlite3_column_int64( ppStmt, 0 );
XP_LOGF( "%s: got a row; id=%lld", __func__, *data );
list = g_slist_append( list, data );
}
break;
case SQLITE_DONE:
sqlite3_finalize( ppStmt );
ppStmt = NULL;
break;
default:
XP_ASSERT( 0 );
break;
}
}
return list;
}
XP_Bool
getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
{
XP_Bool success = XP_FALSE;
const char* fmt = "SELECT room, ended, turn, nmoves, nmissing "
"FROM games WHERE rowid = %lld";
XP_UCHAR query[256];
snprintf( query, sizeof(query), fmt, rowid );
sqlite3_stmt* ppStmt;
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
if ( SQLITE_ROW == result ) {
success = XP_TRUE;
getColumnText( ppStmt, 0, gib->room, sizeof(gib->room) );
gib->gameOver = 1 == sqlite3_column_int( ppStmt, 1 );
gib->turn = sqlite3_column_int( ppStmt, 2 );
gib->nMoves = sqlite3_column_int( ppStmt, 3 );
gib->nMissing = sqlite3_column_int( ppStmt, 4 );
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
}
sqlite3_finalize( ppStmt );
return success;
}
XP_Bool
loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid )
{
char buf[256];
snprintf( buf, sizeof(buf), "SELECT game from games WHERE rowid = %lld", rowid );
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
XP_Bool success = SQLITE_ROW == result;
if ( success ) {
const void* ptr = sqlite3_column_blob( ppStmt, 0 );
int size = sqlite3_column_bytes( ppStmt, 0 );
stream_putBytes( stream, ptr, size );
}
sqlite3_finalize( ppStmt );
return success;
}
void
deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
{
char query[256];
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
sqlite3_stmt* ppStmt;
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
sqlite3_finalize( ppStmt );
}
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 );
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
sqlite3_finalize( ppStmt );
}
XP_Bool
db_fetch( sqlite3* pDb, const gchar* key, gchar* buf, gint buflen )
{
XP_Bool found;
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_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
found = SQLITE_ROW == result;
if ( found ) {
getColumnText( ppStmt, 0, buf, buflen );
} else {
buf[0] = '\0';
}
sqlite3_finalize( ppStmt );
return found;
}
void
db_remove( sqlite3* pDb, const gchar* key )
{
char query[256];
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
sqlite3_stmt *ppStmt;
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
XP_ASSERT( SQLITE_OK == result );
result = sqlite3_step( ppStmt );
XP_ASSERT( SQLITE_DONE == result );
sqlite3_finalize( ppStmt );
}
static void
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';
}

58
xwords4/linux/gamesdb.h Normal file
View file

@ -0,0 +1,58 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000-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) 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 _GAMESDB_H_
#define _GAMESDB_H_
#include <sqlite3.h>
#include <glib.h>
#include "main.h"
#include "comtypes.h"
typedef struct _GameInfo {
XP_UCHAR name[128];
XP_UCHAR room[128];
XP_S16 nMoves;
XP_Bool gameOver;
XP_S16 turn;
XP_S16 nMissing;
} GameInfo;
sqlite3* openGamesDB( const char* dbName );
void closeGamesDB( sqlite3* dbp );
void writeToDB( XWStreamCtxt* stream, void* closure );
void summarize( CommonGlobals* cGlobals );
/* Return GSList whose data is (ptrs to) rowids */
GSList* listGames( sqlite3* dbp );
XP_Bool getGameInfo( sqlite3* dbp, sqlite3_int64 rowid, GameInfo* gib );
XP_Bool loadGame( XWStreamCtxt* stream, sqlite3* pDb, sqlite3_int64 rowid );
void deleteGame( sqlite3* pDb, sqlite3_int64 rowid );
#define KEY_RDEVID "RDEVID"
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 );
#endif

View file

@ -23,7 +23,7 @@
#ifndef _GTKASK_H_ #ifndef _GTKASK_H_
#define _GTKASK_H_ #define _GTKASK_H_
#include "gtkmain.h" #include "gtkboard.h"
/* Returns true for "yes" or "ok" answer, false otherwise. /* Returns true for "yes" or "ok" answer, false otherwise.
*/ */

View file

@ -41,9 +41,9 @@ init_list( GtkWidget* list )
} }
static void static void
add_to_list( GtkWidget *list, const gchar *str ) add_to_list( GtkWidget* list, const gchar* str )
{ {
GtkListStore *store = GtkListStore* store =
GTK_LIST_STORE( gtk_tree_view_get_model(GTK_TREE_VIEW(list))); GTK_LIST_STORE( gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
GtkTreeIter iter; GtkTreeIter iter;
gtk_list_store_append( store, &iter ); gtk_list_store_append( store, &iter );

View file

@ -23,7 +23,7 @@
#ifndef _GTKASKDICT_H_ #ifndef _GTKASKDICT_H_
#define _GTKASKDICT_H_ #define _GTKASKDICT_H_
#include "gtkmain.h" #include "gtkboard.h"
gchar* gtkaskdict( GSList* dicts, gchar* buf, gint buflen ); gchar* gtkaskdict( GSList* dicts, gchar* buf, gint buflen );

2649
xwords4/linux/gtkboard.c Normal file

File diff suppressed because it is too large Load diff

180
xwords4/linux/gtkboard.h Normal file
View file

@ -0,0 +1,180 @@
/* -*- mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* Copyright 1997 - 2005 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 _GTKBOARD_H_
#define _GTKBOARD_H_
#ifdef PLATFORM_GTK
#include <gtk/gtk.h>
#include <sys/time.h>
#include <pango/pango-font.h>
#include <glib.h>
#include "draw.h"
#include "main.h"
#include "game.h"
#include "dictnry.h"
enum {
LAYOUT_BOARD
,LAYOUT_SMALL
,LAYOUT_LARGE
,LAYOUT_NLAYOUTS
};
#define MAX_SCORE_LEN 31
typedef struct GtkDrawCtx {
DrawCtxVTable* vtable;
/* GdkDrawable* pixmap; */
GtkWidget* drawing_area;
struct GtkGameGlobals* globals;
#ifdef USE_CAIRO
cairo_t* cr;
#else
GdkGC* drawGC;
#endif
GdkColor black;
GdkColor white;
GdkColor grey;
GdkColor red; /* for pending tiles */
GdkColor tileBack; /* for pending tiles */
GdkColor cursor;
GdkColor bonusColors[4];
GdkColor playerColors[MAX_NUM_PLAYERS];
/* new for gtk 2.0 */
PangoContext* pangoContext;
GList* fontsPerSize;
struct {
XP_UCHAR str[MAX_SCORE_LEN+1];
XP_U16 fontHt;
} scoreCache[MAX_NUM_PLAYERS];
XP_U16 trayOwner;
XP_U16 cellWidth;
XP_U16 cellHeight;
XP_Bool scoreIsVertical;
} GtkDrawCtx;
typedef struct ClientStreamRec {
XWStreamCtxt* stream;
guint key;
int sock;
} ClientStreamRec;
typedef struct GtkGameGlobals {
CommonGlobals cGlobals;
CurGameInfo gi;
GtkWidget* window;
GtkDrawCtx* draw;
GtkAppGlobals* apg;
/* GdkPixmap* pixmap; */
GtkWidget* drawing_area;
GtkWidget* flip_button;
GtkWidget* zoomin_button;
GtkWidget* zoomout_button;
GtkWidget* toggle_undo_button;
GtkWidget* prevhint_button;
GtkWidget* nexthint_button;
#ifdef XWFEATURE_CHAT
GtkWidget* chat_button;
#endif
EngineCtxt* engine;
guint idleID;
struct timeval scoreTv; /* for timer */
XP_U32 scoreTimerInterval;
GtkAdjustment* adjustment;
ClientStreamRec clientRecs[MAX_NUM_PLAYERS];
guint timerSources[NUM_TIMERS_PLUS_ONE - 1];
#ifndef XWFEATURE_STANDALONE_ONLY
XP_U16 netStatLeft, netStatTop;
XP_UCHAR stateChar;
#endif
XP_Bool gridOn;
XP_Bool dropIncommingMsgs;
XP_Bool mouseDown;
XP_Bool altKeyDown;
#ifdef KEYBOARD_NAV
XP_Bool keyDown;
#endif
} GtkGameGlobals;
/* DictionaryCtxt* gtk_dictionary_make(); */
#define GTK_MIN_SCALE 12 /* was 14 */
#define GTK_MIN_TRAY_SCALEH 24
#define GTK_MIN_TRAY_SCALEV GTK_MIN_TRAY_SCALEH
#define GTK_TRAYPAD_WIDTH 2
#define GTK_TOP_MARGIN 0 /* was 2 */
#define GTK_BOARD_LEFT_MARGIN 2
#define GTK_TRAY_LEFT_MARGIN 2
#define GTK_SCORE_BOARD_PADDING 0
#define GTK_HOR_SCORE_LEFT (GTK_BOARD_LEFT_MARGIN)
#define GTK_HOR_SCORE_HEIGHT 12
#define GTK_TIMER_HEIGHT GTK_HOR_SCORE_HEIGHT
#define GTK_HOR_SCORE_TOP (GTK_TOP_MARGIN)
#define GTK_TIMER_PAD 10
#define GTK_VERT_SCORE_TOP (GTK_TIMER_HEIGHT + GTK_TIMER_PAD)
#define GTK_VERT_SCORE_HEIGHT ((MIN_SCALE*MAX_COLS) - GTK_TIMER_HEIGHT - \
GTK_TIMER_PAD)
#define GTK_TIMER_WIDTH 40
#define GTK_NETSTAT_WIDTH 20
#define GTK_TIMER_TOP GTK_HOR_SCORE_TOP
#define GTK_HOR_SCORE_WIDTH ((GTK_MIN_SCALE*20)-GTK_TIMER_PAD)
#define GTK_VERT_SCORE_WIDTH 40
#define GTK_BOARD_TOP (GTK_SCORE_TOP + GTK_SCORE_HEIGHT \
+ GTK_SCORE_BOARD_PADDING )
#define GTK_BOARD_LEFT (GTK_BOARD_LEFT_MARGIN)
#define GTK_TRAY_LEFT GTK_TRAY_LEFT_MARGIN
#define GTK_DIVIDER_WIDTH 5
#define GTK_BOTTOM_MARGIN GTK_TOP_MARGIN
#define GTK_RIGHT_MARGIN GTK_BOARD_LEFT_MARGIN
void initGlobals( GtkGameGlobals* globals, LaunchParams* params );
void freeGlobals( GtkGameGlobals* globals );
XP_Bool makeNewGame( GtkGameGlobals* globals );
XP_Bool loadGameNoDraw( GtkGameGlobals* globals, LaunchParams* params,
sqlite3_int64 rowid );
void destroy_board_window( GtkWidget* widget, GtkGameGlobals* globals );
#endif /* PLATFORM_GTK */
#endif

View file

@ -22,7 +22,7 @@
#include "gtkchat.h" #include "gtkchat.h"
gchar* gchar*
gtkGetChatMessage( GtkAppGlobals* XP_UNUSED(globals) ) gtkGetChatMessage( GtkGameGlobals* XP_UNUSED(globals) )
{ {
gchar* result = NULL; gchar* result = NULL;
GtkWidget* dialog = gtk_dialog_new_with_buttons( "message text", NULL, //GtkWindow *parent, GtkWidget* dialog = gtk_dialog_new_with_buttons( "message text", NULL, //GtkWindow *parent,

View file

@ -23,9 +23,9 @@
#ifdef PLATFORM_GTK #ifdef PLATFORM_GTK
#include <glib.h> #include <glib.h>
#include "gtkmain.h" #include "gtkboard.h"
gchar* gtkGetChatMessage( GtkAppGlobals* globals ); gchar* gtkGetChatMessage( GtkGameGlobals* globals );
#endif #endif
#endif /* #ifndef _GTKCHAT_H_ */ #endif /* #ifndef _GTKCHAT_H_ */

View file

@ -24,7 +24,7 @@
#include "gtkutils.h" #include "gtkutils.h"
typedef struct _GtkConnsState { typedef struct _GtkConnsState {
GtkAppGlobals* globals; GtkGameGlobals* globals;
CommsAddrRec* addr; CommsAddrRec* addr;
DeviceRole role; DeviceRole role;
@ -179,7 +179,7 @@ makeBTPage( GtkConnsState* state )
} /* makeBTPage */ } /* makeBTPage */
gboolean gboolean
gtkConnsDlg( GtkAppGlobals* globals, CommsAddrRec* addr, DeviceRole role, gtkConnsDlg( GtkGameGlobals* globals, CommsAddrRec* addr, DeviceRole role,
XP_Bool readOnly ) XP_Bool readOnly )
{ {
GtkConnsState state; GtkConnsState state;

View file

@ -24,9 +24,9 @@
#ifndef _GTKCONNSDLG_H_ #ifndef _GTKCONNSDLG_H_
#define _GTKCONNSDLG_H_ #define _GTKCONNSDLG_H_
#include "gtkmain.h" #include "gtkboard.h"
gboolean gtkConnsDlg( GtkAppGlobals* globals, CommsAddrRec* addr, gboolean gtkConnsDlg( GtkGameGlobals* globals, CommsAddrRec* addr,
DeviceRole role, XP_Bool readOnly ); DeviceRole role, XP_Bool readOnly );
#endif /* _GTKCONNSDLG_H_ */ #endif /* _GTKCONNSDLG_H_ */

View file

@ -26,7 +26,7 @@
#include <gdk/gdkdrawable.h> #include <gdk/gdkdrawable.h>
#include "gtkmain.h" #include "gtkboard.h"
#include "draw.h" #include "draw.h"
#include "board.h" #include "board.h"
#include "linuxmain.h" #include "linuxmain.h"
@ -515,7 +515,8 @@ gtk_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* letter,
{ {
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rectInset = *rect; XP_Rect rectInset = *rect;
XP_Bool showGrid = dctx->globals->gridOn; GtkGameGlobals* globals = dctx->globals;
XP_Bool showGrid = globals->gridOn;
XP_Bool highlight = (flags & CELL_HIGHLIGHT) != 0; XP_Bool highlight = (flags & CELL_HIGHLIGHT) != 0;
GdkColor* cursor = GdkColor* cursor =
((flags & CELL_ISCURSOR) != 0) ? &dctx->cursor : NULL; ((flags & CELL_ISCURSOR) != 0) ? &dctx->cursor : NULL;
@ -1305,7 +1306,7 @@ allocAndSet( GdkColormap* map, GdkColor* color, unsigned short red,
} /* allocAndSet */ } /* allocAndSet */
DrawCtx* DrawCtx*
gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals ) gtkDrawCtxtMake( GtkWidget* drawing_area, GtkGameGlobals* globals )
{ {
GtkDrawCtx* dctx = g_malloc0( sizeof(GtkDrawCtx) ); GtkDrawCtx* dctx = g_malloc0( sizeof(GtkDrawCtx) );
GdkColormap* map; GdkColormap* map;
@ -1423,7 +1424,7 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals )
void void
draw_gtk_status( GtkDrawCtx* dctx, char ch ) draw_gtk_status( GtkDrawCtx* dctx, char ch )
{ {
GtkAppGlobals* globals = dctx->globals; GtkGameGlobals* globals = dctx->globals;
XP_Rect rect = { XP_Rect rect = {
.left = globals->netStatLeft, .left = globals->netStatLeft,

View file

@ -22,7 +22,7 @@
#include "draw.h" #include "draw.h"
DrawCtx* gtkDrawCtxtMake( GtkWidget *widget, GtkAppGlobals* globals ); DrawCtx* gtkDrawCtxtMake( GtkWidget *widget, GtkGameGlobals* globals );
void draw_gtk_status( GtkDrawCtx* draw, char ch ); void draw_gtk_status( GtkDrawCtx* draw, char ch );

View file

@ -24,7 +24,7 @@
#ifndef _GTKLETTERASK_H_ #ifndef _GTKLETTERASK_H_
#define _GTKLETTERASK_H_ #define _GTKLETTERASK_H_
#include "gtkmain.h" #include "gtkboard.h"
XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray, XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray,
const XP_UCHAR* name, const XP_UCHAR* name,

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* -*- mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* Copyright 1997 - 2005 by Eric House (xwords@eehouse.org) All rights /* Copyright 1997 - 2013 by Eric House (xwords@eehouse.org) All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -20,155 +20,11 @@
#ifndef _GTKMAIN_H_ #ifndef _GTKMAIN_H_
#define _GTKMAIN_H_ #define _GTKMAIN_H_
#ifdef PLATFORM_GTK
#include <gtk/gtk.h>
#include <sys/time.h>
#include <pango/pango-font.h>
#include <glib.h>
#include "draw.h"
#include "main.h" #include "main.h"
#include "game.h" #include "gtkboard.h"
#include "dictnry.h"
enum { int gtkmain( LaunchParams* params );
LAYOUT_BOARD void windowDestroyed( GtkGameGlobals* globals );
,LAYOUT_SMALL void onGameSaved( void* closure, sqlite3_int64 rowid, XP_Bool firstTime );
,LAYOUT_LARGE
,LAYOUT_NLAYOUTS
};
#define MAX_SCORE_LEN 31
typedef struct GtkDrawCtx {
DrawCtxVTable* vtable;
/* GdkDrawable* pixmap; */
GtkWidget* drawing_area;
struct GtkAppGlobals* globals;
#ifdef USE_CAIRO
cairo_t* cr;
#else
GdkGC* drawGC;
#endif
GdkColor black;
GdkColor white;
GdkColor grey;
GdkColor red; /* for pending tiles */
GdkColor tileBack; /* for pending tiles */
GdkColor cursor;
GdkColor bonusColors[4];
GdkColor playerColors[MAX_NUM_PLAYERS];
/* new for gtk 2.0 */
PangoContext* pangoContext;
GList* fontsPerSize;
struct {
XP_UCHAR str[MAX_SCORE_LEN+1];
XP_U16 fontHt;
} scoreCache[MAX_NUM_PLAYERS];
XP_U16 trayOwner;
XP_U16 cellWidth;
XP_U16 cellHeight;
XP_Bool scoreIsVertical;
} GtkDrawCtx;
typedef struct ClientStreamRec {
XWStreamCtxt* stream;
guint key;
int sock;
} ClientStreamRec;
typedef struct GtkAppGlobals {
CommonGlobals cGlobals;
GtkWidget* window;
GtkDrawCtx* draw;
/* GdkPixmap* pixmap; */
GtkWidget* drawing_area;
GtkWidget* flip_button;
GtkWidget* zoomin_button;
GtkWidget* zoomout_button;
GtkWidget* toggle_undo_button;
GtkWidget* prevhint_button;
GtkWidget* nexthint_button;
#ifdef XWFEATURE_CHAT
GtkWidget* chat_button;
#endif
EngineCtxt* engine;
guint idleID;
struct timeval scoreTv; /* for timer */
XP_U32 scoreTimerInterval;
GtkAdjustment* adjustment;
ClientStreamRec clientRecs[MAX_NUM_PLAYERS];
guint timerSources[NUM_TIMERS_PLUS_ONE - 1];
#ifndef XWFEATURE_STANDALONE_ONLY
XP_U16 netStatLeft, netStatTop;
XP_UCHAR stateChar;
#endif
XP_Bool gridOn;
XP_Bool dropIncommingMsgs;
XP_Bool mouseDown;
XP_Bool altKeyDown;
#ifdef KEYBOARD_NAV
XP_Bool keyDown;
#endif
} GtkAppGlobals;
/* DictionaryCtxt* gtk_dictionary_make(); */
int gtkmain( LaunchParams* params, int argc, char *argv[] );
#define GTK_MIN_SCALE 12 /* was 14 */
#define GTK_MIN_TRAY_SCALEH 24
#define GTK_MIN_TRAY_SCALEV GTK_MIN_TRAY_SCALEH
#define GTK_TRAYPAD_WIDTH 2
#define GTK_TOP_MARGIN 0 /* was 2 */
#define GTK_BOARD_LEFT_MARGIN 2
#define GTK_TRAY_LEFT_MARGIN 2
#define GTK_SCORE_BOARD_PADDING 0
#define GTK_HOR_SCORE_LEFT (GTK_BOARD_LEFT_MARGIN)
#define GTK_HOR_SCORE_HEIGHT 12
#define GTK_TIMER_HEIGHT GTK_HOR_SCORE_HEIGHT
#define GTK_HOR_SCORE_TOP (GTK_TOP_MARGIN)
#define GTK_TIMER_PAD 10
#define GTK_VERT_SCORE_TOP (GTK_TIMER_HEIGHT + GTK_TIMER_PAD)
#define GTK_VERT_SCORE_HEIGHT ((MIN_SCALE*MAX_COLS) - GTK_TIMER_HEIGHT - \
GTK_TIMER_PAD)
#define GTK_TIMER_WIDTH 40
#define GTK_NETSTAT_WIDTH 20
#define GTK_TIMER_TOP GTK_HOR_SCORE_TOP
#define GTK_HOR_SCORE_WIDTH ((GTK_MIN_SCALE*20)-GTK_TIMER_PAD)
#define GTK_VERT_SCORE_WIDTH 40
#define GTK_BOARD_TOP (GTK_SCORE_TOP + GTK_SCORE_HEIGHT \
+ GTK_SCORE_BOARD_PADDING )
#define GTK_BOARD_LEFT (GTK_BOARD_LEFT_MARGIN)
#define GTK_TRAY_LEFT GTK_TRAY_LEFT_MARGIN
#define GTK_DIVIDER_WIDTH 5
#define GTK_BOTTOM_MARGIN GTK_TOP_MARGIN
#define GTK_RIGHT_MARGIN GTK_BOARD_LEFT_MARGIN
#endif /* PLATFORM_GTK */
#endif #endif

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ /* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* /*
* Copyright 2001-2008 by Eric House (xwords@eehouse.org). All rights * Copyright 2001-2013 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -22,16 +22,18 @@
#include <stdarg.h> #include <stdarg.h>
#include "linuxutl.h" #include "linuxutl.h"
#include "linuxmain.h"
#include "gtknewgame.h" #include "gtknewgame.h"
#include "strutils.h" #include "strutils.h"
#include "nwgamest.h" #include "nwgamest.h"
#include "gtkconnsdlg.h" #include "gtkconnsdlg.h"
#include "gtkutils.h" #include "gtkutils.h"
#define MAX_SIZE_CHOICES 10 #define MAX_SIZE_CHOICES 32
typedef struct GtkNewGameState { typedef struct GtkNewGameState {
GtkAppGlobals* globals; GtkGameGlobals* globals;
CurGameInfo* gi;
NewGameCtx* newGameCtxt; NewGameCtx* newGameCtxt;
CommsAddrRec addr; CommsAddrRec addr;
@ -43,6 +45,7 @@ typedef struct GtkNewGameState {
XP_Bool fireConnDlg; XP_Bool fireConnDlg;
gboolean isNewGame; gboolean isNewGame;
short nCols; /* for board size */ short nCols; /* for board size */
gchar* dict;
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
GtkWidget* remoteChecks[MAX_NUM_PLAYERS]; GtkWidget* remoteChecks[MAX_NUM_PLAYERS];
@ -153,6 +156,14 @@ size_combo_changed( GtkComboBox* combo, gpointer gp )
} }
} /* size_combo_changed */ } /* size_combo_changed */
static void
dict_combo_changed( GtkComboBox* combo, gpointer gp )
{
GtkNewGameState* state = (GtkNewGameState*)gp;
state->dict = gtk_combo_box_get_active_text( GTK_COMBO_BOX(combo) );
XP_LOGF( "got dict: %s", state->dict );
} /* size_combo_changed */
static void static void
handle_ok( GtkWidget* XP_UNUSED(widget), gpointer closure ) handle_ok( GtkWidget* XP_UNUSED(widget), gpointer closure )
{ {
@ -199,6 +210,7 @@ makeNewGameDialog( GtkNewGameState* state )
#endif #endif
GtkWidget* nPlayersCombo; GtkWidget* nPlayersCombo;
GtkWidget* boardSizeCombo; GtkWidget* boardSizeCombo;
GtkWidget* dictCombo;
CurGameInfo* gi; CurGameInfo* gi;
short ii; short ii;
@ -237,7 +249,7 @@ makeNewGameDialog( GtkNewGameState* state )
nPlayersCombo = gtk_combo_box_new_text(); nPlayersCombo = gtk_combo_box_new_text();
state->nPlayersCombo = nPlayersCombo; state->nPlayersCombo = nPlayersCombo;
gi = &state->globals->cGlobals.params->gi; gi = state->gi;
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) { for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
char buf[2] = { ii + '1', '\0' }; char buf[2] = { ii + '1', '\0' };
@ -334,12 +346,26 @@ makeNewGameDialog( GtkNewGameState* state )
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Dictionary: "), gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Dictionary: "),
FALSE, TRUE, 0 ); FALSE, TRUE, 0 );
dictCombo = gtk_combo_box_new_text();
g_signal_connect( GTK_OBJECT(dictCombo), "changed",
G_CALLBACK(dict_combo_changed), state );
gtk_widget_show( dictCombo );
gtk_box_pack_start( GTK_BOX(hbox), dictCombo, FALSE, TRUE, 0 );
if ( !!gi->dictName ) { GSList* dicts = listDicts( state->globals->cGlobals.params );
gtk_box_pack_start( GTK_BOX(hbox), GSList* iter;
gtk_label_new(gi->dictName), for ( iter = dicts, ii = 0; !!iter; iter = iter->next, ++ii ) {
FALSE, TRUE, 0 ); const gchar* name = iter->data;
gtk_combo_box_append_text( GTK_COMBO_BOX(dictCombo), name );
if ( !!gi->dictName ) {
if ( !strcmp( name, gi->dictName ) ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
} else if ( 0 == ii ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
} }
g_slist_free( dicts );
gtk_widget_show( hbox ); gtk_widget_show( hbox );
@ -516,17 +542,17 @@ gtk_newgame_attr_set( void* closure, NewGameAttr attr, NGValue value )
} }
gboolean gboolean
newGameDialog( GtkAppGlobals* globals, CommsAddrRec* addr, XP_Bool isNewGame, newGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr,
XP_Bool fireConnDlg ) XP_Bool isNewGame, XP_Bool fireConnDlg )
{ {
GtkNewGameState state; GtkNewGameState state;
XP_MEMSET( &state, 0, sizeof(state) ); XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals; state.globals = globals;
state.newGameCtxt = newg_make( MPPARM(globals->cGlobals.params state.gi = gi;
->util->mpool) state.newGameCtxt = newg_make( MPPARM(globals->cGlobals.util->mpool)
isNewGame, isNewGame,
globals->cGlobals.params->util, globals->cGlobals.util,
gtk_newgame_col_enable, gtk_newgame_col_enable,
gtk_newgame_attr_enable, gtk_newgame_attr_enable,
gtk_newgame_col_get, gtk_newgame_col_get,
@ -542,22 +568,25 @@ newGameDialog( GtkAppGlobals* globals, CommsAddrRec* addr, XP_Bool isNewGame,
state.revert = FALSE; state.revert = FALSE;
state.loaded = XP_FALSE; state.loaded = XP_FALSE;
state.nCols = globals->cGlobals.params->gi.boardSize; state.nCols = gi->boardSize;
state.role = globals->cGlobals.params->gi.serverRole; if ( 0 == state.nCols ) {
state.nCols = globals->cGlobals.params->pgi.boardSize;
}
state.role = gi->serverRole;
XP_MEMCPY( &state.addr, addr, sizeof(state.addr) ); XP_MEMCPY( &state.addr, addr, sizeof(state.addr) );
dialog = makeNewGameDialog( &state ); dialog = makeNewGameDialog( &state );
newg_load( state.newGameCtxt, newg_load( state.newGameCtxt, gi );
&globals->cGlobals.params->gi );
state.loaded = XP_TRUE; state.loaded = XP_TRUE;
gtk_main(); gtk_main();
if ( !state.cancelled && !state.revert ) { if ( !state.cancelled && !state.revert ) {
if ( newg_store( state.newGameCtxt, &globals->cGlobals.params->gi, if ( newg_store( state.newGameCtxt, gi, XP_TRUE ) ) {
XP_TRUE ) ) { gi->boardSize = state.nCols;
globals->cGlobals.params->gi.boardSize = state.nCols; replaceStringIfDifferent( globals->cGlobals.util->mpool,
&gi->dictName, state.dict );
} else { } else {
/* Do it again if we warned user of inconsistency. */ /* Do it again if we warned user of inconsistency. */
state.revert = XP_TRUE; state.revert = XP_TRUE;

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "make MEMDEBUG=TRUE"; -*- */ /* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* /*
* Copyright 2000-2009 by Eric House (xwords@eehouse.org). All rights * Copyright 2000-2013 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -24,10 +24,11 @@
#ifndef _GTKNEWGAME_H_ #ifndef _GTKNEWGAME_H_
#define _GTKNEWGAME_H_ #define _GTKNEWGAME_H_
#include "gtkmain.h" #include "gtkboard.h"
gboolean newGameDialog( GtkAppGlobals* globals, CommsAddrRec* addr, gboolean newGameDialog( GtkGameGlobals* globals, CurGameInfo* gi,
XP_Bool isNewGame, XP_Bool fireConnDlg ); CommsAddrRec* addr, XP_Bool isNewGame,
XP_Bool fireConnDlg );
#endif /* _GTKNEWGAME_H_ */ #endif /* _GTKNEWGAME_H_ */
#endif /* PLATFORM_GTK */ #endif /* PLATFORM_GTK */

View file

@ -23,7 +23,7 @@
#ifndef _GTKNTILESASK_H_ #ifndef _GTKNTILESASK_H_
#define _GTKNTILESASK_H_ #define _GTKNTILESASK_H_
#include "gtkmain.h" #include "gtkboard.h"
XP_U16 askNTiles( XP_U16 nTilesMax, XP_U16 deflt ); XP_U16 askNTiles( XP_U16 nTilesMax, XP_U16 deflt );

View file

@ -24,7 +24,7 @@
#ifndef _GTKUTILS_H_ #ifndef _GTKUTILS_H_
#define _GTKUTILS_H_ #define _GTKUTILS_H_
#include "gtkmain.h" #include "gtkboard.h"
GtkWidget* makeButton( char* text, GCallback func, gpointer data ); GtkWidget* makeButton( char* text, GCallback func, gpointer data );
GtkWidget* makeLabeledField( const char* labelText, GtkWidget** field ); GtkWidget* makeLabeledField( const char* labelText, GtkWidget** field );

View file

@ -360,7 +360,7 @@ linux_bt_open( CommonGlobals* globals, XP_Bool amMaster )
LinBtStuff* btStuff = globals->btStuff; LinBtStuff* btStuff = globals->btStuff;
if ( !btStuff ) { if ( !btStuff ) {
btStuff = globals->btStuff btStuff = globals->btStuff
= lbt_make( MPPARM(globals->params->util->mpool) amMaster ); = lbt_make( MPPARM(globals->util->mpool) amMaster );
btStuff->globals = globals; btStuff->globals = globals;
btStuff->socket = -1; btStuff->socket = -1;
@ -413,7 +413,7 @@ linux_bt_close( CommonGlobals* globals )
(void)close( btStuff->socket ); (void)close( btStuff->socket );
} }
XP_FREE( globals->params->util->mpool, btStuff ); XP_FREE( globals->util->mpool, btStuff );
globals->btStuff = NULL; globals->btStuff = NULL;
} }
} /* linux_bt_close */ } /* linux_bt_close */

29
xwords4/linux/linuxdict.h Normal file
View file

@ -0,0 +1,29 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000 - 2013 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 _LINUXDICT_H_
#define _LINUXDICT_H_
DictionaryCtxt* linux_dictionary_make( MPFORMAL
const LaunchParams* mainParams,
const char* dictFileName,
XP_Bool useMMap );
#endif

File diff suppressed because it is too large Load diff

View file

@ -55,7 +55,7 @@ XP_Bool linuxFireTimer( CommonGlobals* cGlobals, XWTimerReason why );
XWStreamCtxt* stream_from_msgbuf( CommonGlobals* cGlobals, XWStreamCtxt* stream_from_msgbuf( CommonGlobals* cGlobals,
unsigned char* bufPtr, XP_U16 nBytes ); const unsigned char* bufPtr, XP_U16 nBytes );
XP_UCHAR* strFromStream( XWStreamCtxt* stream ); XP_UCHAR* strFromStream( XWStreamCtxt* stream );
void catGameHistory( CommonGlobals* cGlobals ); void catGameHistory( CommonGlobals* cGlobals );
@ -73,8 +73,6 @@ XP_Bool getDictPath( const LaunchParams *params, const char* name,
GSList* listDicts( const LaunchParams *params ); GSList* listDicts( const LaunchParams *params );
void saveGame( CommonGlobals* cGlobals ); void saveGame( CommonGlobals* cGlobals );
int blocking_read( int fd, unsigned char* buf, int len );
void linux_close_socket( CommonGlobals* cGlobals ); void linux_close_socket( CommonGlobals* cGlobals );
#ifdef KEYBOARD_NAV #ifdef KEYBOARD_NAV
@ -94,4 +92,23 @@ void setOneSecondTimer( CommonGlobals* cGlobals );
# define setOneSecondTimer( cGlobals ) # define setOneSecondTimer( cGlobals )
#endif #endif
void setupLinuxUtilCallbacks( XW_UtilCtxt* util );
void initFromParams( CommonGlobals* cGlobals, LaunchParams* params );
void setupUtil( CommonGlobals* cGlobals );
DictionaryCtxt* makeDictForStream( CommonGlobals* cGlobals,
XWStreamCtxt* stream );
void linuxSetIsServer( CommonGlobals* cGlobals, XP_Bool isServer );
void linuxChangeRoles( CommonGlobals* cGlobals );
void sendRelayReg( LaunchParams* params, sqlite3* pDb );
void gameGotBuf( CommonGlobals* globals, XP_Bool haveDraw,
const XP_U8* buf, XP_U16 len );
gboolean app_socket_proc( GIOChannel* source, GIOCondition condition,
gpointer data );
const XP_UCHAR* linux_getDevID( LaunchParams* params, DevIDType* typ );
/* void initParams( LaunchParams* params ); */
/* void freeParams( LaunchParams* params ); */
#endif #endif

View file

@ -1,4 +1,4 @@
/* -*-mode: C; compile-command: "make -j MEMDEBUG=TRUE";-*- */ /* -*- compile-command: "make -j MEMDEBUG=TRUE";-*- */
/* /*
* Copyright 2006-2009 by Eric House (xwords@eehouse.org). All rights * Copyright 2006-2009 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
@ -93,7 +93,7 @@ linux_sms_init( CommonGlobals* globals, const CommsAddrRec* addr )
{ {
LinSMSData* data = globals->smsData; LinSMSData* data = globals->smsData;
if ( !data ) { if ( !data ) {
data = XP_MALLOC( globals->params->util->mpool, sizeof(*data) ); data = XP_MALLOC( globals->util->mpool, sizeof(*data) );
XP_ASSERT( !!data ); XP_ASSERT( !!data );
XP_MEMSET( data, 0, sizeof(*data) ); XP_MEMSET( data, 0, sizeof(*data) );
globals->smsData = data; globals->smsData = data;
@ -125,7 +125,7 @@ linux_sms_close( CommonGlobals* globals )
{ {
LinSMSData* data = globals->smsData; LinSMSData* data = globals->smsData;
if ( !!data ) { if ( !!data ) {
XP_FREE( globals->params->util->mpool, data ); XP_FREE( globals->util->mpool, data );
globals->smsData = NULL; globals->smsData = NULL;
} }
} /* linux_sms_close */ } /* linux_sms_close */

View file

@ -31,6 +31,8 @@
#include "linuxutl.h" #include "linuxutl.h"
#include "main.h" #include "main.h"
#include "linuxdict.h"
#include "linuxmain.h"
#include "LocalizedStrIncludes.h" #include "LocalizedStrIncludes.h"
#ifdef DEBUG #ifdef DEBUG
@ -148,7 +150,7 @@ setSquareBonuses( const CommonGlobals* cGlobals )
{ {
XP_U16 nBonuses; XP_U16 nBonuses;
XWBonusType* bonuses = XWBonusType* bonuses =
bonusesFor( cGlobals->params->gi.boardSize, &nBonuses ); bonusesFor( cGlobals->gi->boardSize, &nBonuses );
if ( !!bonuses ) { if ( !!bonuses ) {
model_setSquareBonuses( cGlobals->game.model, bonuses, nBonuses ); model_setSquareBonuses( cGlobals->game.model, bonuses, nBonuses );
} }
@ -348,19 +350,8 @@ 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;
if ( !!cGlobals->params->rDevID ) { return linux_getDevID( cGlobals->params, typ );
*typ = ID_TYPE_RELAY;
result = cGlobals->params->rDevID;
} else if ( !!cGlobals->params->devID ) {
*typ = ID_TYPE_LINUX;
result = cGlobals->params->devID;
} else {
*typ = ID_TYPE_NONE;
result = NULL;
}
return result;
} }
static void static void
@ -390,6 +381,9 @@ linux_util_deviceRegistered( XW_UtilCtxt* uc, DevIDType typ,
void void
linux_util_vt_init( MPFORMAL XW_UtilCtxt* util ) linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
{ {
#ifdef MEM_DEBUG
util->mpool = mpool;
#endif
util->vtable = XP_MALLOC( mpool, sizeof(UtilVtable) ); util->vtable = XP_MALLOC( mpool, sizeof(UtilVtable) );
util->vtable->m_util_makeEmptyDict = linux_util_makeEmptyDict; util->vtable->m_util_makeEmptyDict = linux_util_makeEmptyDict;
@ -611,7 +605,7 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd )
XP_ASSERT( 0 < nMsgs ); XP_ASSERT( 0 < nMsgs );
XWStreamCtxt* stream = XWStreamCtxt* stream =
mem_stream_make( MPPARM(cGlobals->params->util->mpool) mem_stream_make( MPPARM(cGlobals->util->mpool)
cGlobals->params->vtMgr, cGlobals->params->vtMgr,
cGlobals, CHANNEL_NONE, NULL ); cGlobals, CHANNEL_NONE, NULL );
stream_putU16( stream, 1 ); /* number of relayIDs */ stream_putU16( stream, 1 ); /* number of relayIDs */

View file

@ -32,9 +32,6 @@ void linux_debugf(const char*, ...)
__attribute__ ((format (printf, 1, 2))); __attribute__ ((format (printf, 1, 2)));
#endif #endif
DictionaryCtxt* linux_dictionary_make( MPFORMAL const LaunchParams* mainParams,
const char* dictFileName, XP_Bool useMMap );
void linux_util_vt_init( MPFORMAL XW_UtilCtxt* util ); void linux_util_vt_init( MPFORMAL XW_UtilCtxt* util );
void linux_util_vt_destroy( XW_UtilCtxt* util ); void linux_util_vt_destroy( XW_UtilCtxt* util );
@ -49,6 +46,7 @@ XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len,
const XP_UCHAR* relayID ); const XP_UCHAR* relayID );
void writeNoConnMsgs( CommonGlobals* cGlobals, int fd ); void writeNoConnMsgs( CommonGlobals* cGlobals, int fd );
#ifdef STREAM_VERS_BIGBOARD #ifdef STREAM_VERS_BIGBOARD
void setSquareBonuses( const CommonGlobals* cGlobals ); void setSquareBonuses( const CommonGlobals* cGlobals );
#else #else

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* /*
* Copyright 2001-2007 by Eric House (xwords@eehouse.org). All rights * Copyright 2001-2013 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -25,6 +25,8 @@
# include <bluetooth/bluetooth.h> /* for bdaddr_t, which should move */ # include <bluetooth/bluetooth.h> /* for bdaddr_t, which should move */
#endif #endif
#include <sqlite3.h>
#include "comtypes.h" #include "comtypes.h"
#include "util.h" #include "util.h"
#include "game.h" #include "game.h"
@ -42,26 +44,33 @@ typedef struct LinuxUtilCtxt {
UtilVtable* vtable; UtilVtable* vtable;
} LinuxUtilCtxt; } LinuxUtilCtxt;
typedef void (*SockReceiver)( void* closure, int socket );
typedef void (*NewSocketProc)( void* closure, int newSock, int oldSock,
SockReceiver proc, void* procClosure );
typedef struct LaunchParams { typedef struct LaunchParams {
/* CommPipeCtxt* pipe; */ /* CommPipeCtxt* pipe; */
XW_UtilCtxt* util; CurGameInfo pgi;
DictionaryCtxt* dict;
CurGameInfo gi;
PlayerDicts dicts;
GSList* dictDirs; GSList* dictDirs;
char* fileName; char* fileName;
char* dbName;
sqlite3* pDb; /* null unless opened */
XP_U16 saveFailPct; XP_U16 saveFailPct;
const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS]; const XP_UCHAR* playerDictNames[MAX_NUM_PLAYERS];
#ifdef USE_SQLITE #ifdef USE_SQLITE
char* dbFileName; char* dbFileName;
XP_U32 dbFileID; XP_U32 dbFileID;
#endif #endif
void* relayConStorage; /* opaque outside of relaycon.c */
char* pipe; char* pipe;
char* nbs; char* nbs;
char* bonusFile; char* bonusFile;
#ifdef XWFEATURE_DEVID #ifdef XWFEATURE_DEVID
char* devID; char* devID;
char* rDevID; char* rDevID;
XP_Bool noAnonDevid;
XP_UCHAR devIDStore[16];
#endif #endif
VTableMgr* vtMgr; VTableMgr* vtMgr;
XP_U16 nLocalPlayers; XP_U16 nLocalPlayers;
@ -140,7 +149,7 @@ typedef struct LaunchParams {
ServerInfo serverInfo; ServerInfo serverInfo;
ClientInfo clientInfo; ClientInfo clientInfo;
} info; } info;
MPSLOT
} LaunchParams; } LaunchParams;
typedef struct CommonGlobals CommonGlobals; typedef struct CommonGlobals CommonGlobals;
@ -165,17 +174,29 @@ typedef struct _TimerInfo {
#endif #endif
} TimerInfo; } TimerInfo;
typedef void (*OnSaveFunc)( void* closure, sqlite3_int64 rowid,
XP_Bool firstTime );
struct CommonGlobals { struct CommonGlobals {
LaunchParams* params; LaunchParams* params;
CommonPrefs cp; CommonPrefs cp;
XW_UtilCtxt* util;
XWGame game; XWGame game;
CurGameInfo* gi;
CommsAddrRec addr;
DictionaryCtxt* dict;
PlayerDicts dicts;
XP_U16 lastNTilesToUse; XP_U16 lastNTilesToUse;
XP_U16 lastStreamSize; XP_U16 lastStreamSize;
XP_Bool manualFinal; /* use asked for final scores */ XP_Bool manualFinal; /* use asked for final scores */
sqlite3* pDb;
sqlite3_int64 selRow;
SocketChangedFunc socketChanged; SocketChangedFunc socketChanged;
void* socketChangedClosure; void* socketChangedClosure;
OnSaveFunc onSave;
void* onSaveClosure;
GSList* packetQueue; GSList* packetQueue;
XP_U32 nextPacketID; /* for debugging */ XP_U32 nextPacketID; /* for debugging */
@ -210,4 +231,24 @@ struct CommonGlobals {
XP_U16 curSaveToken; XP_U16 curSaveToken;
}; };
typedef struct _SourceData {
GIOChannel* channel;
gint watch;
SockReceiver proc;
void* procClosure;
} SourceData;
typedef struct _GtkAppGlobals {
GArray* selRows;
LaunchParams* params;
GSList* globalsList;
GList* sources;
GtkWidget* window;
GtkWidget* listWidget;
GtkWidget* openButton;
GtkWidget* deleteButton;
} GtkAppGlobals;
sqlite3_int64 getSelRow( const GtkAppGlobals* apg );
#endif #endif

364
xwords4/linux/relaycon.c Normal file
View file

@ -0,0 +1,364 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2013 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 <netdb.h>
#include <errno.h>
#include <stdbool.h>
#include "relaycon.h"
#include "comtypes.h"
typedef struct _RelayConStorage {
int socket;
RelayConnProcs procs;
void* procsClosure;
struct sockaddr_in saddr;
} RelayConStorage;
typedef struct _MsgHeader {
XWRelayReg cmd;
uint32_t packetID;
} MsgHeader;
static RelayConStorage* getStorage( LaunchParams* params );
static XP_U32 hostNameToIP( const XP_UCHAR* name );
static void relaycon_receive( void* closure, int socket );
static ssize_t sendIt( RelayConStorage* storage, const XP_U8* msgbuf, XP_U16 len );
static size_t addStrWithLength( XP_U8* buf, XP_U8* end, const XP_UCHAR* str );
static void getNetString( const XP_U8** ptr, XP_U16 len, XP_UCHAR* buf );
static XP_U16 getNetShort( const XP_U8** ptr );
static XP_U32 getNetLong( const XP_U8** ptr );
static int writeHeader( XP_U8* dest, XWRelayReg cmd );
static bool readHeader( const XP_U8** buf, MsgHeader* header );
static size_t writeDevID( XP_U8* buf, size_t len, const XP_UCHAR* str );
void
relaycon_init( LaunchParams* params, const RelayConnProcs* procs,
void* procsClosure, const char* host, int port )
{
XP_ASSERT( !params->relayConStorage );
RelayConStorage* storage = getStorage( params );
XP_MEMCPY( &storage->procs, procs, sizeof(storage->procs) );
storage->procsClosure = procsClosure;
storage->socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
(*procs->socketChanged)( procsClosure, storage->socket, -1,
relaycon_receive, params );
XP_MEMSET( &storage->saddr, 0, sizeof(storage->saddr) );
storage->saddr.sin_family = PF_INET;
storage->saddr.sin_addr.s_addr = htonl( hostNameToIP(host) );
storage->saddr.sin_port = htons(port);
}
void
relaycon_reg( LaunchParams* params, const XP_UCHAR* devID, DevIDType typ )
{
LOG_FUNC();
XP_U8 tmpbuf[32];
int indx = 0;
RelayConStorage* storage = getStorage( params );
XP_ASSERT( !!devID || typ == ID_TYPE_ANON );
indx += writeHeader( tmpbuf, XWPDEV_REG );
tmpbuf[indx++] = typ;
indx += writeDevID( &tmpbuf[indx], sizeof(tmpbuf) - indx, devID );
sendIt( storage, tmpbuf, indx );
}
XP_S16
relaycon_send( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
XP_U32 gameToken, const CommsAddrRec* XP_UNUSED(addrRec) )
{
XP_ASSERT( 0 != gameToken );
ssize_t nSent = -1;
RelayConStorage* storage = getStorage( params );
XP_U8 tmpbuf[1 + 4 + 1 + sizeof(gameToken) + buflen];
int indx = 0;
indx += writeHeader( tmpbuf, XWPDEV_MSG );
XP_U32 inNBO = htonl(gameToken);
XP_MEMCPY( &tmpbuf[indx], &inNBO, sizeof(inNBO) );
indx += sizeof(inNBO);
XP_MEMCPY( &tmpbuf[indx], buf, buflen );
indx += buflen;
nSent = sendIt( storage, tmpbuf, indx );
if ( nSent > buflen ) {
nSent = buflen;
}
LOG_RETURNF( "%d", nSent );
return nSent;
}
XP_S16
relaycon_sendnoconn( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
const XP_UCHAR* relayID, XP_U32 gameToken )
{
XP_LOGF( "%s(relayID=%s)", __func__, relayID );
XP_ASSERT( 0 != gameToken );
XP_U16 indx = 0;
ssize_t nSent = -1;
RelayConStorage* storage = getStorage( params );
XP_U16 idLen = XP_STRLEN( relayID );
XP_U8 tmpbuf[1 + 4 + 1 +
1 + idLen +
sizeof(gameToken) + buflen];
indx += writeHeader( tmpbuf, XWPDEV_MSGNOCONN );
gameToken = htonl( gameToken );
XP_MEMCPY( &tmpbuf[indx], &gameToken, sizeof(gameToken) );
indx += sizeof(gameToken);
XP_MEMCPY( &tmpbuf[indx], relayID, idLen );
indx += idLen;
tmpbuf[indx++] = '\n';
XP_MEMCPY( &tmpbuf[indx], buf, buflen );
nSent = sendIt( storage, tmpbuf, sizeof(tmpbuf) );
if ( nSent > buflen ) {
nSent = buflen;
}
LOG_RETURNF( "%d", nSent );
return nSent;
}
void
relaycon_requestMsgs( LaunchParams* params, const XP_UCHAR* devID )
{
XP_LOGF( "%s(devID=%s)", __func__, devID );
RelayConStorage* storage = getStorage( params );
XP_U8 tmpbuf[128];
int indx = 0;
indx += writeHeader( tmpbuf, XWPDEV_RQSTMSGS );
indx += addStrWithLength( &tmpbuf[indx], tmpbuf + sizeof(tmpbuf), devID );
sendIt( storage, tmpbuf, indx );
}
void
relaycon_deleted( LaunchParams* params, const XP_UCHAR* devID,
XP_U32 gameToken )
{
LOG_FUNC();
RelayConStorage* storage = getStorage( params );
XP_U8 tmpbuf[128];
int indx = 0;
indx += writeHeader( tmpbuf, XWPDEV_DELGAME );
indx += writeDevID( &tmpbuf[indx], sizeof(tmpbuf) - indx, devID );
gameToken = htonl( gameToken );
memcpy( &tmpbuf[indx], &gameToken, sizeof(gameToken) );
indx += sizeof( gameToken );
sendIt( storage, tmpbuf, indx );
}
static void
sendAckIf( RelayConStorage* storage, const MsgHeader* header )
{
if ( header->cmd != XWPDEV_ACK ) {
XP_U8 tmpbuf[16];
int indx = writeHeader( tmpbuf, XWPDEV_ACK );
uint32_t msgID = htonl( header->packetID );
memcpy( &tmpbuf[indx], &msgID, sizeof(msgID) );
indx += sizeof(msgID);
sendIt( storage, tmpbuf, indx );
}
}
static void
relaycon_receive( void* closure, int socket )
{
LaunchParams* params = (LaunchParams*)closure;
XP_ASSERT( !!params->relayConStorage );
RelayConStorage* storage = getStorage( params );
XP_U8 buf[512];
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
XP_LOGF( "%s: calling recvfrom on socket %d", __func__, socket );
ssize_t nRead = recvfrom( socket, buf, sizeof(buf), 0, /* flags */
(struct sockaddr*)&from, &fromlen );
XP_LOGF( "%s: read %d bytes", __func__, nRead );
if ( 0 <= nRead ) {
const XP_U8* ptr = buf;
const XP_U8* end = buf + nRead;
MsgHeader header;
if ( readHeader( &ptr, &header ) ) {
sendAckIf( storage, &header );
switch( header.cmd ) {
case XWPDEV_REGRSP: {
XP_U16 len = getNetShort( &ptr );
XP_UCHAR devID[len+1];
getNetString( &ptr, len, devID );
(*storage->procs.devIDChanged)( storage->procsClosure, devID );
}
break;
case XWPDEV_MSG:
(*storage->procs.msgReceived)( storage->procsClosure,
ptr, end - ptr );
break;
case XWPDEV_BADREG:
(*storage->procs.devIDChanged)( storage->procsClosure, NULL );
break;
case XWPDEV_HAVEMSGS: {
(*storage->procs.msgNoticeReceived)( storage->procsClosure );
break;
}
case XWPDEV_ALERT: {
XP_U16 len = getNetShort( &ptr );
XP_UCHAR buf[len+1];
getNetString( &ptr, len, buf );
(*storage->procs.msgErrorMsg)( storage->procsClosure, buf );
break;
}
case XWPDEV_ACK: {
XP_U32 packetID = getNetLong( &ptr );
XP_LOGF( "got ack for packetID %ld", packetID );
break;
}
default:
XP_LOGF( "%s: Unexpected cmd %d", __func__, header.cmd );
XP_ASSERT( 0 );
}
}
} else {
XP_LOGF( "%s: error reading udp socket: %d (%s)", __func__,
errno, strerror(errno) );
}
}
void
relaycon_cleanup( LaunchParams* params )
{
XP_FREEP( params->mpool, &params->relayConStorage );
}
static RelayConStorage*
getStorage( LaunchParams* params )
{
RelayConStorage* storage = (RelayConStorage*)params->relayConStorage;
if ( NULL == storage ) {
storage = XP_CALLOC( params->mpool, sizeof(*storage) );
storage->socket = -1;
params->relayConStorage = storage;
}
return storage;
}
static XP_U32
hostNameToIP( const XP_UCHAR* name )
{
XP_U32 ip;
struct hostent* host;
XP_LOGF( "%s: looking up %s", __func__, name );
host = gethostbyname( name );
if ( NULL == host ) {
XP_WARNF( "gethostbyname returned NULL\n" );
} else {
XP_MEMCPY( &ip, host->h_addr_list[0], sizeof(ip) );
ip = ntohl(ip);
}
XP_LOGF( "%s found %lx for %s", __func__, ip, name );
return ip;
}
static ssize_t
sendIt( RelayConStorage* storage, const XP_U8* msgbuf, XP_U16 len )
{
ssize_t nSent = sendto( storage->socket, msgbuf, len, 0, /* flags */
(struct sockaddr*)&storage->saddr,
sizeof(storage->saddr) );
XP_LOGF( "%s()=>%d", __func__, nSent );
return nSent;
}
static size_t
addStrWithLength( XP_U8* buf, XP_U8* end, const XP_UCHAR* str )
{
XP_U16 len = !!str? XP_STRLEN( str ) : 0;
if ( buf + len + sizeof(len) <= end ) {
XP_U16 lenNBO = htons( len );
XP_MEMCPY( buf, &lenNBO, sizeof(lenNBO) );
buf += sizeof(lenNBO);
XP_MEMCPY( buf, str, len );
}
return len + sizeof(len);
}
static size_t
writeDevID( XP_U8* buf, size_t len, const XP_UCHAR* str )
{
return addStrWithLength( buf, buf + len, str );
}
static XP_U16
getNetShort( const XP_U8** ptr )
{
XP_U16 result;
memcpy( &result, *ptr, sizeof(result) );
*ptr += sizeof(result);
return ntohs( result );
}
static XP_U32
getNetLong( const XP_U8** ptr )
{
XP_U32 result;
memcpy( &result, *ptr, sizeof(result) );
*ptr += sizeof(result);
return ntohl( result );
}
static void
getNetString( const XP_U8** ptr, XP_U16 len, XP_UCHAR* buf )
{
memcpy( buf, *ptr, len );
*ptr += len;
buf[len] = '\0';
}
static int
writeHeader( XP_U8* dest, XWRelayReg cmd )
{
int indx = 0;
dest[indx++] = XWPDEV_PROTO_VERSION;
uint32_t packetNum = htonl(0);
memcpy( &dest[indx], &packetNum, sizeof(packetNum) );
indx += sizeof(packetNum);
dest[indx++] = cmd;
return indx;
}
static bool
readHeader( const XP_U8** buf, MsgHeader* header )
{
const XP_U8* ptr = *buf;
bool ok = XWPDEV_PROTO_VERSION == *ptr++;
assert( ok );
uint32_t packetID;
memcpy( &packetID, ptr, sizeof(packetID) );
ptr += sizeof(packetID);
header->packetID = ntohl( packetID );
XP_LOGF( "%s: got packet %d", __func__, header->packetID );
header->cmd = *ptr++;
*buf = ptr;
return ok;
}

47
xwords4/linux/relaycon.h Normal file
View file

@ -0,0 +1,47 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2013 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 _RELAYCON_H_
#define _RELAYCON_H_
#include "main.h"
typedef struct _Procs {
void (*msgReceived)( void* closure, const XP_U8* buf, XP_U16 len );
void (*msgNoticeReceived)( void* closure );
void (*devIDChanged)( void* closure, const XP_UCHAR* devID );
void (*msgErrorMsg)( void* closure, const XP_UCHAR* msg );
void (*socketChanged)( void* closure, int newSock, int oldSock,
SockReceiver proc, void* procClosure );
} RelayConnProcs;
void relaycon_init( LaunchParams* params, const RelayConnProcs* procs,
void* procsClosure, const char* host, int port );
void relaycon_reg( LaunchParams* params, const XP_UCHAR* devID, DevIDType typ );
XP_S16 relaycon_send( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
XP_U32 gameID, const CommsAddrRec* addrRec );
XP_S16 relaycon_sendnoconn( LaunchParams* params, const XP_U8* buf, XP_U16 buflen,
const XP_UCHAR* relayID, XP_U32 gameToken );
void relaycon_requestMsgs( LaunchParams* params, const XP_UCHAR* devID );
void relaycon_deleted( LaunchParams* params, const XP_UCHAR* devID,
XP_U32 gameToken );
void relaycon_cleanup( LaunchParams* params );
#endif

View file

@ -6,6 +6,7 @@ APP_NEW=""
DO_CLEAN="" DO_CLEAN=""
APP_NEW_PARAMS="" APP_NEW_PARAMS=""
NGAMES="" NGAMES=""
UDP_PCT=0
UPGRADE_ODDS="" UPGRADE_ODDS=""
NROOMS="" NROOMS=""
HOST="" HOST=""
@ -188,7 +189,13 @@ build_cmds() {
DEV=0 DEV=0
for NLOCALS in ${LOCALS[@]}; do for NLOCALS in ${LOCALS[@]}; do
DEV=$((DEV + 1)) DEV=$((DEV + 1))
FILE="${LOGDIR}/GAME_${GAME}_${DEV}.xwg" if [ $((RANDOM % 100)) -gt $UDP_PCT ]; then
FILE="${LOGDIR}/GAME_${GAME}_${DEV}.xwg"
USE_UDP=""
else
FILE="${LOGDIR}/GAME_${GAME}_${DEV}.sql3"
USE_UDP=1
fi
LOG=${LOGDIR}/${GAME}_${DEV}_LOG.txt LOG=${LOGDIR}/${GAME}_${DEV}_LOG.txt
> $LOG # clear the log > $LOG # clear the log
@ -209,10 +216,15 @@ build_cmds() {
PARAMS="$PARAMS $BOARD_SIZE --room $ROOM --trade-pct 20 --sort-tiles " PARAMS="$PARAMS $BOARD_SIZE --room $ROOM --trade-pct 20 --sort-tiles "
[ $UNDO_PCT -gt 0 ] && PARAMS="$PARAMS --undo-pct $UNDO_PCT " [ $UNDO_PCT -gt 0 ] && PARAMS="$PARAMS --undo-pct $UNDO_PCT "
PARAMS="$PARAMS --game-dict $DICT --port $PORT --host $HOST " PARAMS="$PARAMS --game-dict $DICT --port $PORT --host $HOST "
PARAMS="$PARAMS --file $FILE --slow-robot 1:3 --skip-confirm" PARAMS="$PARAMS --slow-robot 1:3 --skip-confirm"
if [ -n "$USE_UDP" ]; then
PARAMS="$PARAMS --db $FILE"
else
PARAMS="$PARAMS --file $FILE"
fi
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS" PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
# PARAMS="$PARAMS --split-packets 2" # PARAMS="$PARAMS --split-packets 2"
if [ -n $SEND_CHAT ]; then if [ -n "$SEND_CHAT" ]; then
PARAMS="$PARAMS --send-chat $SEND_CHAT" PARAMS="$PARAMS --send-chat $SEND_CHAT"
fi fi
# PARAMS="$PARAMS --savefail-pct 10" # PARAMS="$PARAMS --savefail-pct 10"
@ -316,7 +328,7 @@ kill_from_log() {
if [ -n "$RELAYID" ]; then if [ -n "$RELAYID" ]; then
OBITS="$OBITS -d $RELAYID" OBITS="$OBITS -d $RELAYID"
if [ 0 -eq $(($RANDOM%2)) ]; then if [ 0 -eq $(($RANDOM%2)) ]; then
../relay/rq -a $HOST $OBITS 2>/dev/null || true ../relay/rq -a $HOST $OBITS 2>/dev/null || /bin/true
OBITS="" OBITS=""
fi fi
return 0 # success return 0 # success
@ -332,7 +344,7 @@ maybe_resign() {
if grep -q XWRELAY_ALLHERE $LOG; then if grep -q XWRELAY_ALLHERE $LOG; then
if [ 0 -eq $(($RANDOM % $RESIGN_RATIO)) ]; then if [ 0 -eq $(($RANDOM % $RESIGN_RATIO)) ]; then
echo "making $LOG $(connName $LOG) resign..." echo "making $LOG $(connName $LOG) resign..."
kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" || true kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" || /bin/true
fi fi
fi fi
fi fi
@ -380,18 +392,18 @@ check_game() {
# kill_from_logs $OTHERS $KEY # kill_from_logs $OTHERS $KEY
for ID in $OTHERS $KEY; do for ID in $OTHERS $KEY; do
echo -n "${LOGS[$ID]}, " echo -n "${LOGS[$ID]}, "
kill_from_log ${LOGS[$ID]} || true kill_from_log ${LOGS[$ID]} || /bin/true
close_device $ID $DONEDIR "game over" close_device $ID $DONEDIR "game over"
done done
echo "" echo ""
# XWRELAY_ERROR_DELETED may be old # XWRELAY_ERROR_DELETED may be old
elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then
echo "deleting $LOG $(connName $LOG) b/c another resigned" echo "deleting $LOG $(connName $LOG) b/c another resigned"
kill_from_log $LOG || true kill_from_log $LOG || /bin/true
close_device $KEY $DEADDIR "other resigned" close_device $KEY $DEADDIR "other resigned"
elif grep -q 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then elif grep -q 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then
echo "deleting $LOG $(connName $LOG) b/c another resigned" echo "deleting $LOG $(connName $LOG) b/c another resigned"
kill_from_log $LOG || true kill_from_log $LOG || /bin/true
close_device $KEY $DEADDIR "other resigned" close_device $KEY $DEADDIR "other resigned"
else else
maybe_resign $KEY maybe_resign $KEY
@ -423,7 +435,7 @@ get_relayid() {
update_devid_cmd() { update_devid_cmd() {
KEY=$1 KEY=$1
HELP="$(${APPS[$KEY]} --help 2>&1 || true)" HELP="$(${APPS[$KEY]} --help 2>&1 || /bin/true)"
if echo $HELP | grep -q '\-\-devid'; then if echo $HELP | grep -q '\-\-devid'; then
CMD="--devid LINUX_TEST_$(printf %.5d ${KEY})" CMD="--devid LINUX_TEST_$(printf %.5d ${KEY})"
LOG=${LOGS[$KEY]} LOG=${LOGS[$KEY]}
@ -467,7 +479,8 @@ run_cmds() {
try_upgrade $KEY try_upgrade $KEY
launch $KEY & launch $KEY &
PID=$! PID=$!
renice +1 $PID >/dev/null # renice doesn't work on one of my machines...
renice -n 1 -p $PID >/dev/null 2>&1 || /bin/true
PIDS[$KEY]=$PID PIDS[$KEY]=$PID
ROOM_PIDS[$ROOM]=$PID ROOM_PIDS[$ROOM]=$PID
MINEND[$KEY]=$(($NOW + $MINRUN)) MINEND[$KEY]=$(($NOW + $MINRUN))
@ -476,7 +489,7 @@ run_cmds() {
if [ -d /proc/$PID ]; then if [ -d /proc/$PID ]; then
SLEEP=$((${MINEND[$KEY]} - $NOW)) SLEEP=$((${MINEND[$KEY]} - $NOW))
[ $SLEEP -gt 0 ] && sleep $SLEEP [ $SLEEP -gt 0 ] && sleep $SLEEP
kill $PID || true kill $PID || /bin/true
wait $PID wait $PID
fi fi
PIDS[$KEY]=0 PIDS[$KEY]=0
@ -487,7 +500,7 @@ run_cmds() {
fi fi
done done
[ -n "$OBITS" ] && ../relay/rq -a $HOST $OBITS 2>/dev/null || true [ -n "$OBITS" ] && ../relay/rq -a $HOST $OBITS 2>/dev/null || /bin/true
# kill any remaining games # kill any remaining games
if [ $COUNT -gt 0 ]; then if [ $COUNT -gt 0 ]; then
@ -528,7 +541,7 @@ run_via_rq() {
launch $KEY & launch $KEY &
PID=$! PID=$!
sleep 2 sleep 2
kill $PID || true kill $PID || /bin/true
wait $PID wait $PID
fi fi
[ "$DROP_N" -ge 0 ] && increment_drop $KEY [ "$DROP_N" -ge 0 ] && increment_drop $KEY
@ -544,6 +557,7 @@ function getArg() {
function usage() { function usage() {
[ $# -gt 0 ] && echo "Error: $1" >&2 [ $# -gt 0 ] && echo "Error: $1" >&2
echo "Usage: $(basename $0) \\" >&2 echo "Usage: $(basename $0) \\" >&2
echo " [--via-udp <pct>] \\" >&2
echo " [--clean-start] \\" >&2 echo " [--clean-start] \\" >&2
echo " [--game-dict <path/to/dict>]* \\" >&2 echo " [--game-dict <path/to/dict>]* \\" >&2
echo " [--old-app <path/to/app]* \\" >&2 echo " [--old-app <path/to/app]* \\" >&2
@ -571,6 +585,10 @@ function usage() {
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
case $1 in case $1 in
--via-udp)
UDP_PCT=$(getArg $*)
shift
;;
--clean-start) --clean-start)
DO_CLEAN=1 DO_CLEAN=1
;; ;;
@ -626,14 +644,14 @@ while [ "$#" -gt 0 ]; do
UNDO_PCT=$(getArg $*) UNDO_PCT=$(getArg $*)
shift shift
;; ;;
--send-chat) --send-chat)
SEND_CHAT=$(getArg $*) SEND_CHAT=$(getArg $*)
shift shift
;; ;;
--resign-ratio) --resign-ratio)
RESIGN_RATIO=$(getArg $*) RESIGN_RATIO=$(getArg $*)
shift shift
;; ;;
--help) --help)
usage usage
;; ;;

View file

@ -0,0 +1,7 @@
#!/bin/sh
# Show pids of processes holding onto the xwgames DB in some way
# from http://newstrib.com/main.asp?SectionID=2&SubSectionID=27&ArticleID=26068
echo "select pg_class.relname,pg_locks.* from pg_class,pg_locks where pg_class.relfilenode=pg_locks.relation;" | psql xwgames

View file

@ -1053,7 +1053,7 @@ handlePutMessage( SafeCref& scr, HostID hid, const AddrInfo* addr,
if ( getNetByte( bufp, end, &cmd ) if ( getNetByte( bufp, end, &cmd )
&& getNetByte( bufp, end, &src ) && getNetByte( bufp, end, &src )
&& getNetByte( bufp, end, &dest ) ) { && getNetByte( bufp, end, &dest ) ) {
success = true; // meaning, buffer content looks ok success = true; // meaning, buffer content looks ok
*bufp = start + len; *bufp = start + len;
if ( ( cmd == XWRELAY_MSG_TORELAY_NOCONN ) && ( hid == dest ) ) { if ( ( cmd == XWRELAY_MSG_TORELAY_NOCONN ) && ( hid == dest ) ) {
scr.PutMsg( src, addr, dest, start, len ); scr.PutMsg( src, addr, dest, start, len );
@ -1092,7 +1092,7 @@ handleProxyMsgs( int sock, const AddrInfo* addr, const unsigned char* bufp,
unsigned short nMsgs; unsigned short nMsgs;
if ( getNetShort( &bufp, end, &nMsgs ) ) { if ( getNetShort( &bufp, end, &nMsgs ) ) {
SafeCref scr( connName ); SafeCref scr( connName );
while ( nMsgs-- > 0 ) { while ( scr.IsValid() && nMsgs-- > 0 ) {
unsigned short len; unsigned short len;
if ( getNetShort( &bufp, end, &len ) ) { if ( getNetShort( &bufp, end, &len ) ) {
if ( handlePutMessage( scr, hid, addr, len, &bufp, end ) ) { if ( handlePutMessage( scr, hid, addr, len, &bufp, end ) ) {