diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml
index d0872209d..44a98ef7d 100644
--- a/xwords4/android/XWords4/res/xml/xwprefs.xml
+++ b/xwords4/android/XWords4/res/xml/xwprefs.xml
@@ -168,13 +168,13 @@
android:defaultValue="false"
/>
-
-
-
-
-
-
-
+
gi->players[newPlayer]))) {
PerTurnInfo* newInfo = &board->pti[newPlayer];
XP_U16 oldPlayer = board->selPlayer;
model_foreachPendingCell( board->model, newPlayer,
diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c
index 8d961cc8a..100160050 100644
--- a/xwords4/common/comms.c
+++ b/xwords4/common/comms.c
@@ -284,7 +284,9 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer,
MPASSIGN(result->mpool, mpool);
result->isServer = isServer;
- XP_MEMCPY( &result->procs, procs, sizeof(result->procs) );
+ if ( !!procs ) {
+ XP_MEMCPY( &result->procs, procs, sizeof(result->procs) );
+ }
result->util = util;
#ifdef XWFEATURE_RELAY
@@ -1308,9 +1310,13 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
cookieID, srcID, destID );
/* If these values don't check out, drop it */
- XP_ASSERT( COMMS_RELAYSTATE_ALLCONNECTED == comms->r.relayState
- || COMMS_RELAYSTATE_CONNECTED == comms->r.relayState
- || COMMS_RELAYSTATE_RECONNECTED == comms->r.relayState );
+ /* When a message comes in via proxy (rather than a connection) state
+ may not be as expected. Just commenting these out is probably the
+ wrong fix. Maybe instead the constructor takes a flag that means
+ "assume you're connected" Revisit this. */
+ /* XP_ASSERT( COMMS_RELAYSTATE_ALLCONNECTED == comms->r.relayState */
+ /* || COMMS_RELAYSTATE_CONNECTED == comms->r.relayState */
+ /* || COMMS_RELAYSTATE_RECONNECTED == comms->r.relayState ); */
if ( destID == comms->r.myHostID ) { /* When would this not happen? */
consumed = XP_FALSE;
diff --git a/xwords4/common/game.c b/xwords4/common/game.c
index 1ab0b26d5..0ee1ed425 100644
--- a/xwords4/common/game.c
+++ b/xwords4/common/game.c
@@ -229,7 +229,9 @@ game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game,
gi->nPlayers );
server_prefsChanged( game->server, cp );
board_prefsChanged( game->board, cp );
- draw_dictChanged( draw, dict );
+ if ( !!draw ) {
+ draw_dictChanged( draw, dict );
+ }
success = XP_TRUE;
} while( XP_FALSE );
}
diff --git a/xwords4/dawg/.gitignore b/xwords4/dawg/.gitignore
index 82f214b31..4935774d4 100644
--- a/xwords4/dawg/.gitignore
+++ b/xwords4/dawg/.gitignore
@@ -2,5 +2,6 @@
*.xwd
*.pdb
*.stamp
+*.dict
*.dict.gz
dict2dawg
diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c
index 34ed7f8c0..673692333 100644
--- a/xwords4/linux/cursesmain.c
+++ b/xwords4/linux/cursesmain.c
@@ -1458,12 +1458,12 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
g_globals.cGlobals.socketChangedClosure = &g_globals;
g_globals.cGlobals.addAcceptor = curses_socket_acceptor;
- g_globals.cp.showBoardArrow = XP_TRUE;
- g_globals.cp.showRobotScores = params->showRobotScores;
- g_globals.cp.hideTileValues = params->hideValues;
+ g_globals.cGlobals.cp.showBoardArrow = XP_TRUE;
+ g_globals.cGlobals.cp.showRobotScores = params->showRobotScores;
+ g_globals.cGlobals.cp.hideTileValues = params->hideValues;
#ifdef XWFEATURE_SLOW_ROBOT
- g_globals.cp.robotThinkMin = params->robotThinkMin;
- g_globals.cp.robotThinkMax = params->robotThinkMax;
+ g_globals.cGlobals.cp.robotThinkMin = params->robotThinkMin;
+ g_globals.cGlobals.cp.robotThinkMax = params->robotThinkMax;
#endif
dict = params->dict;
@@ -1490,136 +1490,141 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
struct sigaction act2 = { .sa_handler = SIGWINCH_handler };
sigaction( SIGWINCH, &act2, NULL );
- initCurses( &g_globals );
- getmaxyx( g_globals.boardWin, height, width );
+ if ( !!params->pipe && !!params->fileName ) {
+ read_pipe_then_close( &g_globals.cGlobals );
+ } else {
- g_globals.draw = (struct CursesDrawCtx*)
- cursesDrawCtxtMake( g_globals.boardWin );
+ initCurses( &g_globals );
+ getmaxyx( g_globals.boardWin, height, width );
- TransportProcs procs = {
- .closure = &g_globals,
- .send = LINUX_SEND,
+ g_globals.draw = (struct CursesDrawCtx*)
+ cursesDrawCtxtMake( g_globals.boardWin );
+
+ TransportProcs procs = {
+ .closure = &g_globals,
+ .send = LINUX_SEND,
#ifdef COMMS_HEARTBEAT
- .reset = linux_reset,
+ .reset = linux_reset,
#endif
#ifdef XWFEATURE_RELAY
- .rstatus = relay_status_curses,
- .rconnd = relay_connd_curses,
- .rerror = relay_error_curses,
+ .rstatus = relay_status_curses,
+ .rconnd = relay_connd_curses,
+ .rerror = relay_error_curses,
#endif
- };
+ };
- if ( !!params->fileName && file_exists( params->fileName ) ) {
- XWStreamCtxt* stream;
- stream = streamFromFile( &g_globals.cGlobals, params->fileName,
- &g_globals );
+ if ( !!params->fileName && file_exists( params->fileName ) ) {
+ XWStreamCtxt* stream;
+ stream = streamFromFile( &g_globals.cGlobals, params->fileName,
+ &g_globals );
- (void)game_makeFromStream( MEMPOOL stream, &g_globals.cGlobals.game,
- ¶ms->gi, dict, params->util,
- (DrawCtx*)g_globals.draw,
- &g_globals.cp, &procs );
+ (void)game_makeFromStream( MEMPOOL stream, &g_globals.cGlobals.game,
+ ¶ms->gi, dict, params->util,
+ (DrawCtx*)g_globals.draw,
+ &g_globals.cGlobals.cp, &procs );
- stream_destroy( stream );
+ stream_destroy( stream );
- if ( !isServer && params->gi.serverRole == SERVER_ISSERVER ) {
- isServer = XP_TRUE;
+ if ( !isServer && params->gi.serverRole == SERVER_ISSERVER ) {
+ isServer = XP_TRUE;
+ }
+ } else {
+ game_makeNewGame( MEMPOOL &g_globals.cGlobals.game, ¶ms->gi,
+ params->util, (DrawCtx*)g_globals.draw,
+ &g_globals.cGlobals.cp, &procs );
}
- } else {
- game_makeNewGame( MEMPOOL &g_globals.cGlobals.game, ¶ms->gi,
- params->util, (DrawCtx*)g_globals.draw,
- &g_globals.cp, &procs );
- }
#ifndef XWFEATURE_STANDALONE_ONLY
- if ( g_globals.cGlobals.game.comms ) {
- CommsAddrRec addr = {0};
+ if ( g_globals.cGlobals.game.comms ) {
+ CommsAddrRec addr = {0};
- if ( 0 ) {
+ if ( 0 ) {
# ifdef XWFEATURE_RELAY
- } else if ( params->conType == COMMS_CONN_RELAY ) {
- addr.conType = COMMS_CONN_RELAY;
- addr.u.ip_relay.ipAddr = 0; /* ??? */
- addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort;
- addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom;
- addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom;
- XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName,
- sizeof(addr.u.ip_relay.hostName) - 1 );
- XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite,
- sizeof(addr.u.ip_relay.invite) - 1 );
+ } else if ( params->conType == COMMS_CONN_RELAY ) {
+ addr.conType = COMMS_CONN_RELAY;
+ addr.u.ip_relay.ipAddr = 0; /* ??? */
+ addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort;
+ addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom;
+ addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom;
+ XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName,
+ sizeof(addr.u.ip_relay.hostName) - 1 );
+ XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite,
+ sizeof(addr.u.ip_relay.invite) - 1 );
# endif
# ifdef XWFEATURE_SMS
- } else if ( params->conType == COMMS_CONN_SMS ) {
- addr.conType = COMMS_CONN_SMS;
- XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.serverPhone,
- sizeof(addr.u.sms.phone) - 1 );
- addr.u.sms.port = params->connInfo.sms.port;
+ } else if ( params->conType == COMMS_CONN_SMS ) {
+ addr.conType = COMMS_CONN_SMS;
+ XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.serverPhone,
+ sizeof(addr.u.sms.phone) - 1 );
+ addr.u.sms.port = params->connInfo.sms.port;
# endif
# ifdef XWFEATURE_BLUETOOTH
- } else if ( params->conType == COMMS_CONN_BT ) {
- addr.conType = COMMS_CONN_BT;
- XP_ASSERT( sizeof(addr.u.bt.btAddr)
- >= sizeof(params->connInfo.bt.hostAddr));
- XP_MEMCPY( &addr.u.bt.btAddr, ¶ms->connInfo.bt.hostAddr,
- sizeof(params->connInfo.bt.hostAddr) );
+ } else if ( params->conType == COMMS_CONN_BT ) {
+ addr.conType = COMMS_CONN_BT;
+ XP_ASSERT( sizeof(addr.u.bt.btAddr)
+ >= sizeof(params->connInfo.bt.hostAddr));
+ XP_MEMCPY( &addr.u.bt.btAddr, ¶ms->connInfo.bt.hostAddr,
+ sizeof(params->connInfo.bt.hostAddr) );
# endif
+ }
+ comms_setAddr( g_globals.cGlobals.game.comms, &addr );
}
- comms_setAddr( g_globals.cGlobals.game.comms, &addr );
- }
#endif
- model_setDictionary( g_globals.cGlobals.game.model, params->dict );
+ model_setDictionary( g_globals.cGlobals.game.model, params->dict );
- positionSizeStuff( &g_globals, width, height );
+ positionSizeStuff( &g_globals, width, height );
#ifndef XWFEATURE_STANDALONE_ONLY
- /* send any events that need to get off before the event loop begins */
- if ( !isServer ) {
- if ( 1 /* stream_open( params->info.clientInfo.stream ) */) {
- server_initClientConnection( g_globals.cGlobals.game.server,
- mem_stream_make( MEMPOOL
- params->vtMgr,
- &g_globals,
- (XP_PlayerAddr)0,
- sendOnClose ) );
- } else {
- cursesUserError( &g_globals, "Unable to open connection to server");
- exit( 0 );
+ /* send any events that need to get off before the event loop begins */
+ if ( !isServer ) {
+ if ( 1 /* stream_open( params->info.clientInfo.stream ) */) {
+ server_initClientConnection( g_globals.cGlobals.game.server,
+ mem_stream_make( MEMPOOL
+ params->vtMgr,
+ &g_globals,
+ (XP_PlayerAddr)0,
+ sendOnClose ) );
+ } else {
+ cursesUserError( &g_globals, "Unable to open connection to server");
+ exit( 0 );
+ }
}
- }
#endif
- server_do( g_globals.cGlobals.game.server );
+ server_do( g_globals.cGlobals.game.server );
- g_globals.menuList = g_boardMenuList;
- drawMenuLargeOrSmall( &g_globals, g_boardMenuList );
- board_draw( g_globals.cGlobals.game.board );
+ g_globals.menuList = g_boardMenuList;
+ drawMenuLargeOrSmall( &g_globals, g_boardMenuList );
+ board_draw( g_globals.cGlobals.game.board );
- while ( !g_globals.timeToExit ) {
- int ch = 0;
- if ( blocking_gotEvent( &g_globals, &ch ) ) {
- remapKey( &ch );
- if (
+ while ( !g_globals.timeToExit ) {
+ int ch = 0;
+ if ( blocking_gotEvent( &g_globals, &ch ) ) {
+ remapKey( &ch );
+ if (
#ifdef CURSES_SMALL_SCREEN
- handleKeyEvent( &g_globals, g_rootMenuListShow, ch ) ||
+ handleKeyEvent( &g_globals, g_rootMenuListShow, ch ) ||
#endif
- handleKeyEvent( &g_globals, g_globals.menuList, ch )
- || handleKeyEvent( &g_globals, g_sharedMenuList, ch )
- || passKeyToBoard( &g_globals, ch ) ) {
- if ( g_globals.doDraw ) {
- board_draw( g_globals.cGlobals.game.board );
- g_globals.doDraw = XP_FALSE;
+ handleKeyEvent( &g_globals, g_globals.menuList, ch )
+ || handleKeyEvent( &g_globals, g_sharedMenuList, ch )
+ || passKeyToBoard( &g_globals, ch ) ) {
+ if ( g_globals.doDraw ) {
+ board_draw( g_globals.cGlobals.game.board );
+ g_globals.doDraw = XP_FALSE;
+ }
}
}
}
- }
+ }
if ( !!g_globals.cGlobals.params->fileName ) {
XWStreamCtxt* outStream;
- outStream = mem_stream_make(
- MPPARM(g_globals.cGlobals.params->util->mpool)
- g_globals.cGlobals.params->vtMgr,
- &g_globals.cGlobals, 0, writeToFile );
+ outStream =
+ mem_stream_make( MPPARM(g_globals.cGlobals.params->util->mpool)
+ g_globals.cGlobals.params->vtMgr,
+ &g_globals.cGlobals, 0, writeToFile );
stream_open( outStream );
game_saveToStream( &g_globals.cGlobals.game,
diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h
index fe9984265..62b05506d 100644
--- a/xwords4/linux/cursesmain.h
+++ b/xwords4/linux/cursesmain.h
@@ -58,7 +58,6 @@ struct CursesAppGlobals {
DictionaryCtxt* dictionary;
EngineCtxt* engine;
- CommonPrefs cp;
XP_Bool amServer; /* this process acting as server */
diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c
index 2a3a485e8..83530042e 100644
--- a/xwords4/linux/gtkmain.c
+++ b/xwords4/linux/gtkmain.c
@@ -39,6 +39,8 @@
#endif
#include
#include
+#include
+#include
#include "main.h"
#include "linuxmain.h"
@@ -419,7 +421,7 @@ createOrLoadObjects( GtkAppGlobals* globals )
&globals->cGlobals.params->gi,
params->dict, params->util,
(DrawCtx*)globals->draw,
- &globals->cp, &procs );
+ &globals->cGlobals.cp, &procs );
stream_destroy( stream );
}
@@ -440,7 +442,7 @@ createOrLoadObjects( GtkAppGlobals* globals )
game_makeNewGame( MEMPOOL &globals->cGlobals.game, ¶ms->gi,
params->util, (DrawCtx*)globals->draw,
- &globals->cp, &procs );
+ &globals->cGlobals.cp, &procs );
addr.conType = params->conType;
if ( 0 ) {
@@ -782,7 +784,7 @@ new_game_impl( GtkAppGlobals* globals, XP_Bool fireConnDlg )
game_reset( MEMPOOL &globals->cGlobals.game, gi,
globals->cGlobals.params->util,
- &globals->cp, &procs );
+ &globals->cGlobals.cp, &procs );
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!globals->cGlobals.game.comms ) {
@@ -1878,7 +1880,7 @@ newConnectionInput( GIOChannel *source,
GIOCondition condition,
gpointer data )
{
- gboolean keepSource;
+ gboolean keepSource = TRUE;
int sock = g_io_channel_unix_get_fd( source );
GtkAppGlobals* globals = (GtkAppGlobals*)data;
@@ -1886,25 +1888,7 @@ newConnectionInput( GIOChannel *source,
/* XP_ASSERT( sock == globals->cGlobals.socket ); */
- if ( (condition & (G_IO_HUP | G_IO_ERR)) != 0 ) {
- XP_LOGF( "dropping socket %d", sock );
- close( sock );
-#ifdef XWFEATURE_RELAY
- globals->cGlobals.socket = -1;
-#endif
- if ( 0 ) {
-#ifdef XWFEATURE_BLUETOOTH
- } else if ( COMMS_CONN_BT == globals->cGlobals.params->conType ) {
- linux_bt_socketclosed( &globals->cGlobals, sock );
-#endif
-#ifdef XWFEATURE_IP_DIRECT
- } else if ( COMMS_CONN_IP_DIRECT == globals->cGlobals.params->conType ) {
- linux_udp_socketclosed( &globals->cGlobals, sock );
-#endif
- }
- keepSource = FALSE; /* remove the event source */
-
- } else if ( (condition & G_IO_IN) != 0 ) {
+ if ( (condition & G_IO_IN) != 0 ) {
ssize_t nRead;
unsigned char buf[512];
CommsAddrRec* addrp = NULL;
@@ -1972,8 +1956,27 @@ newConnectionInput( GIOChannel *source,
} else {
XP_LOGF( "errno from read: %d/%s", errno, strerror(errno) );
}
- keepSource = TRUE;
}
+
+ if ( (condition & (G_IO_HUP | G_IO_ERR)) != 0 ) {
+ XP_LOGF( "dropping socket %d", sock );
+ close( sock );
+#ifdef XWFEATURE_RELAY
+ globals->cGlobals.socket = -1;
+#endif
+ if ( 0 ) {
+#ifdef XWFEATURE_BLUETOOTH
+ } else if ( COMMS_CONN_BT == globals->cGlobals.params->conType ) {
+ linux_bt_socketclosed( &globals->cGlobals, sock );
+#endif
+#ifdef XWFEATURE_IP_DIRECT
+ } else if ( COMMS_CONN_IP_DIRECT == globals->cGlobals.params->conType ) {
+ linux_udp_socketclosed( &globals->cGlobals, sock );
+#endif
+ }
+ keepSource = FALSE; /* remove the event source */
+ }
+
return keepSource; /* FALSE means to remove event source */
} /* newConnectionInput */
@@ -2144,16 +2147,16 @@ gtkmain( LaunchParams* params, int argc, char *argv[] )
globals.cGlobals.addAcceptor = gtk_socket_acceptor;
#endif
- globals.cp.showBoardArrow = XP_TRUE;
- globals.cp.hideTileValues = params->hideValues;
- globals.cp.skipCommitConfirm = params->skipCommitConfirm;
- globals.cp.sortNewTiles = params->sortNewTiles;
- globals.cp.showColors = params->showColors;
- globals.cp.allowPeek = params->allowPeek;
- globals.cp.showRobotScores = params->showRobotScores;
+ globals.cGlobals.cp.showBoardArrow = XP_TRUE;
+ globals.cGlobals.cp.hideTileValues = params->hideValues;
+ globals.cGlobals.cp.skipCommitConfirm = params->skipCommitConfirm;
+ globals.cGlobals.cp.sortNewTiles = params->sortNewTiles;
+ globals.cGlobals.cp.showColors = params->showColors;
+ globals.cGlobals.cp.allowPeek = params->allowPeek;
+ globals.cGlobals.cp.showRobotScores = params->showRobotScores;
#ifdef XWFEATURE_SLOW_ROBOT
- globals.cp.robotThinkMin = params->robotThinkMin;
- globals.cp.robotThinkMax = params->robotThinkMax;
+ globals.cGlobals.cp.robotThinkMin = params->robotThinkMin;
+ globals.cGlobals.cp.robotThinkMax = params->robotThinkMax;
#endif
setupGtkUtilCallbacks( &globals, params->util );
@@ -2259,11 +2262,14 @@ gtkmain( LaunchParams* params, int argc, char *argv[] )
/* | GDK_POINTER_MOTION_HINT_MASK */
);
- gtk_widget_show( window );
+ if ( !!params->pipe && !!params->fileName ) {
+ read_pipe_then_close( &globals.cGlobals );
+ } else {
+ gtk_widget_show( window );
- gtk_main();
-
-/* MONCONTROL(1); */
+ gtk_main();
+ }
+ /* MONCONTROL(1); */
cleanup( &globals );
diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h
index cfb02563a..eb5f9c931 100644
--- a/xwords4/linux/gtkmain.h
+++ b/xwords4/linux/gtkmain.h
@@ -119,8 +119,6 @@ typedef struct GtkAppGlobals {
XP_UCHAR stateChar;
#endif
- CommonPrefs cp;
-
XP_Bool gridOn;
XP_Bool dropIncommingMsgs;
XP_Bool mouseDown;
diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c
index 2dbed7ddf..776eb5aab 100644
--- a/xwords4/linux/linuxmain.c
+++ b/xwords4/linux/linuxmain.c
@@ -185,6 +185,57 @@ strFromStream( XWStreamCtxt* stream )
return buf;
} /* strFromStream */
+void
+read_pipe_then_close( CommonGlobals* cGlobals )
+{
+ LaunchParams* params = cGlobals->params;
+ XWStreamCtxt* stream =
+ streamFromFile( cGlobals, params->fileName, cGlobals );
+
+ XP_Bool opened = game_makeFromStream( MPPARM(cGlobals->params->util->mpool)
+ stream, &cGlobals->game,
+ ¶ms->gi,
+ params->dict, params->util,
+ NULL /*draw*/,
+ &cGlobals->cp, NULL );
+ XP_ASSERT( opened );
+ stream_destroy( stream );
+
+ XP_Bool handled = XP_FALSE;
+ int fd = open( params->pipe, O_RDONLY );
+ while ( fd >= 0 ) {
+ unsigned short len;
+ ssize_t nRead = blocking_read( fd, (unsigned char*)&len, sizeof(len) );
+ if ( nRead != 2 ) {
+ break;
+ }
+ len = ntohs( len );
+ unsigned char buf[len];
+ nRead = blocking_read( fd, buf, len );
+ if ( nRead != len ) {
+ break;
+ }
+ stream = mem_stream_make( MPPARM(cGlobals->params->util->mpool)
+ params->vtMgr, cGlobals, CHANNEL_NONE, NULL );
+ stream_putBytes( stream, buf, len );
+
+ if ( comms_checkIncomingStream( cGlobals->game.comms,
+ stream, NULL ) ) {
+ handled = server_receiveMessage( cGlobals->game.server,
+ stream ) || handled;
+ }
+ stream_destroy( stream );
+ }
+ LOG_RETURNF( "%d", handled );
+
+ /* Write it out */
+ /* stream = mem_stream_make( MEMPOOLCG(cGlobals) params->vtMgr, */
+ /* cGlobals, 0, writeToFile ); */
+ /* stream_open( stream ); */
+ /* game_saveToStream( &cGlobals->game, ¶ms->gi, stream ); */
+ /* stream_destroy( stream ); */
+} /* read_pipe_then_close */
+
typedef enum {
CMD_SKIP_GAMEOVER
,CMD_SHOW_OTHERSCORES
@@ -216,6 +267,8 @@ typedef enum {
,CMD_HIDEVALUES
,CMD_SKIPCONFIRM
,CMD_VERTICALSCORE
+ ,CMD_NOPEEK
+ ,CMD_ADDPIPE
#ifdef XWFEATURE_SEARCHLIMIT
,CMD_HINTRECT
#endif
@@ -283,6 +336,8 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_HIDEVALUES, false, "hide-values", "show letters, not nums, on tiles" }
,{ CMD_SKIPCONFIRM, false, "skip-confirm", "don't confirm before commit" }
,{ CMD_VERTICALSCORE, false, "vertical", "scoreboard is vertical" }
+ ,{ CMD_NOPEEK, false, "no-peek", "disallow scoreboard tap changing player" }
+ ,{ CMD_ADDPIPE, true, "with-pipe", "named pipe to listen on for relay msgs" }
#ifdef XWFEATURE_SEARCHLIMIT
,{ CMD_HINTRECT, false, "hintrect", "enable draggable hint-limits rect" }
#endif
@@ -581,13 +636,28 @@ linux_close_socket( CommonGlobals* cGlobals )
close( socket );
}
+int
+blocking_read( int fd, unsigned char* buf, int len )
+{
+ int nRead = 0;
+ while ( nRead < len ) {
+ ssize_t siz = read( fd, buf + nRead, len - nRead );
+ if ( siz <= 0 ) {
+ nRead = -1;
+ break;
+ }
+ nRead += siz;
+ }
+ return nRead;
+}
+
int
linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize )
{
int sock = cGlobals->socket;
unsigned short tmp;
unsigned short packetSize;
- ssize_t nRead = recv( sock, &tmp, sizeof(tmp), 0 );
+ ssize_t nRead = blocking_read( sock, (unsigned char*)&tmp, sizeof(tmp) );
if ( nRead != 2 ) {
XP_LOGF( "recv => %d, errno=%d (\"%s\")", nRead, errno, strerror(errno) );
linux_close_socket( cGlobals );
@@ -597,7 +667,7 @@ linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize )
packetSize = ntohs( tmp );
assert( packetSize <= bufSize );
- nRead = recv( sock, buf, packetSize, 0 );
+ nRead = blocking_read( sock, buf, packetSize );
if ( nRead < 0 ) {
XP_WARNF( "%s: errno=%d (\"%s\")\n", __func__, errno,
strerror(errno) );
@@ -1044,10 +1114,15 @@ main( int argc, char** argv )
case CMD_VERTICALSCORE:
mainParams.verticalScore = XP_TRUE;
break;
+ case CMD_NOPEEK:
+ mainParams.allowPeek = XP_FALSE;
+ case CMD_ADDPIPE:
+ mainParams.pipe = optarg;
+ break;
#ifdef XWFEATURE_SLOW_ROBOT
case CMD_SLOWROBOT:
if ( !parsePair( optarg, &mainParams.robotThinkMin,
- &mainParams.robotThinkMax ) ) {
+ &mainParams.robotThinkMax ) ) {
usage(argv[0], "bad param" );
}
break;
diff --git a/xwords4/linux/linuxmain.h b/xwords4/linux/linuxmain.h
index a14a9de62..b9ae258d2 100644
--- a/xwords4/linux/linuxmain.h
+++ b/xwords4/linux/linuxmain.h
@@ -64,6 +64,8 @@ XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name,
void* closure );
void writeToFile( XWStreamCtxt* stream, void* closure );
+int blocking_read( int fd, unsigned char* buf, int len );
+
void linux_close_socket( CommonGlobals* cGlobals );
#ifdef KEYBOARD_NAV
@@ -72,4 +74,6 @@ XP_Bool linShiftFocus( CommonGlobals* cGlobals, XP_Key key,
BoardObjectType* nxtP );
#endif
+void read_pipe_then_close( CommonGlobals* cGlobals );
+
#endif
diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h
index b7dabf5e2..fcef37a60 100644
--- a/xwords4/linux/main.h
+++ b/xwords4/linux/main.h
@@ -48,6 +48,7 @@ typedef struct LaunchParams {
DictionaryCtxt* dict;
CurGameInfo gi;
char* fileName;
+ char* pipe;
VTableMgr* vtMgr;
XP_U16 nLocalPlayers;
XP_U16 nHidden;
@@ -136,6 +137,7 @@ typedef struct _TimerInfo {
struct CommonGlobals {
LaunchParams* params;
+ CommonPrefs cp;
XWGame game;
XP_U16 lastNTilesToUse;
diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh
index 55b213fc7..6565c8806 100755
--- a/xwords4/linux/scripts/discon_ok2.sh
+++ b/xwords4/linux/scripts/discon_ok2.sh
@@ -128,12 +128,14 @@ launch() {
close_device() {
ID=$1
MVTO=$2
+ REASON="$3"
if [ ${PIDS[$ID]} -ne 0 ]; then
kill ${PIDS[$ID]} 2>/dev/null
wait ${PIDS[$ID]}
fi
unset PIDS[$ID]
unset CMDS[$ID]
+ echo "closing game: $REASON" >> ${LOGS[$ID]}
if [ -n "$MVTO" ]; then
[ -f ${FILES[$ID]} ] && mv ${FILES[$ID]} $MVTO
mv ${LOGS[$ID]} $MVTO
@@ -178,7 +180,7 @@ maybe_resign() {
if grep -q XWRELAY_ALLHERE $LOG; then
if [ 0 -eq $(($RANDOM % $RESIGN_RATIO)) ]; then
echo "making $LOG $(connName $LOG) resign..."
- kill_from_log $LOG && close_device $KEY $DEADDIR
+ kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced"
fi
fi
fi
@@ -213,13 +215,13 @@ check_game() {
for ID in $OTHERS $KEY; do
echo -n "${LOGS[$ID]}, "
kill_from_log ${LOGS[$ID]} || true
- close_device $ID $DONEDIR
+ close_device $ID $DONEDIR "game over"
done
date
elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then
echo "deleting $LOG $(connName $LOG) b/c another resigned"
kill_from_log $LOG || true
- close_device $KEY $DEADDIR
+ close_device $KEY $DEADDIR "other resigned"
else
maybe_resign $KEY
fi
@@ -239,7 +241,7 @@ run_cmds() {
PIDS[$KEY]=$!
else
sleep 2 # make sure it's had some time
- kill ${PIDS[$KEY]}
+ kill ${PIDS[$KEY]} || true
PIDS[$KEY]=0
check_game $KEY
fi
@@ -250,7 +252,7 @@ run_cmds() {
mkdir -p ${LOGDIR}/not_done
echo "processing unfinished games...."
for KEY in ${!CMDS[*]}; do
- close_device $KEY ${LOGDIR}/not_done
+ close_device $KEY ${LOGDIR}/not_done "unfinished game"
done
fi
}
@@ -266,4 +268,10 @@ run_cmds
print_stats
wait
-echo "*********$0 finished: $(date) (took $(($(date +%s)-$STARTTIME)) seconds)**************"
+
+SECONDS=$(($(date +%s)-$STARTTIME))
+HOURS=$((SECONDS/3600))
+SECONDS=$((SECONDS%3600))
+MINUTES=$((SECONDS/60))
+SECONDS=$((SECONDS%60))
+echo "*********$0 finished: $(date) (took $HOURS:$MINUTES:$SECONDS)**************"
diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile
index 7d4e94827..1ea89de5f 100644
--- a/xwords4/relay/Makefile
+++ b/xwords4/relay/Makefile
@@ -41,9 +41,10 @@ OBJ = $(patsubst %.cpp,%.o,$(SRC))
LDFLAGS += -pthread -g $(STATIC) \
-L$(shell pg_config --libdir) -lpq
-CPPFLAGS += -DSPAWN_SELF -DDO_HTTP -g -Wall \
+CPPFLAGS += -DSPAWN_SELF -g -Wall \
-I $(shell pg_config --includedir) \
-DSVN_REV=\"$(shell cat $(GITINFO) 2>/dev/null || echo -n $(HASH) )\"
+# CPPFLAGS += -DDO_HTTP
# turn on semaphore debugging
# CPPFLAGS += -DDEBUG_LOCKS
diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp
index 6fb537e15..6676526b1 100644
--- a/xwords4/relay/dbmgr.cpp
+++ b/xwords4/relay/dbmgr.cpp
@@ -434,14 +434,15 @@ DBMgr::StoreMessage( const char* const connName, int hid,
const unsigned char* buf, int len )
{
size_t newLen;
- const char* fmt = "INSERT INTO " MSGS_TABLE " (connname, hid, msg)"
- " VALUES( '%s', %d, E'%s')";
+ const char* fmt = "INSERT INTO " MSGS_TABLE " (connname, hid, msg, msglen)"
+ " VALUES( '%s', %d, E'%s', %d)";
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, len, &newLen );
assert( NULL != bytes );
char query[newLen+128];
- unsigned int siz = snprintf( query, sizeof(query), fmt, connName, hid, bytes );
+ unsigned int siz = snprintf( query, sizeof(query), fmt, connName, hid,
+ bytes, len );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
PQfreemem( bytes );
assert( siz < sizeof(query) );
@@ -450,13 +451,14 @@ DBMgr::StoreMessage( const char* const connName, int hid,
}
bool
-DBMgr::GetStoredMessage( const char* const connName, int hid,
- unsigned char* buf, size_t* buflen, int* msgID )
+DBMgr::GetNthStoredMessage( const char* const connName, int hid,
+ int nn, unsigned char* buf, size_t* buflen,
+ int* msgID )
{
- const char* fmt = "SELECT id, msg FROM " MSGS_TABLE
- " WHERE connName = '%s' AND hid = %d ORDER BY id LIMIT 1";
+ const char* fmt = "SELECT id, msg, msglen FROM " MSGS_TABLE
+ " WHERE connName = '%s' AND hid = %d ORDER BY id LIMIT 1 OFFSET %d";
char query[256];
- snprintf( query, sizeof(query), fmt, connName, hid );
+ snprintf( query, sizeof(query), fmt, connName, hid, nn );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
PGresult* result = PQexec( getThreadConn(), query );
@@ -465,7 +467,10 @@ DBMgr::GetStoredMessage( const char* const connName, int hid,
bool found = nTuples == 1;
if ( found ) {
- *msgID = atoi( PQgetvalue( result, 0, 0 ) );
+ if ( NULL != msgID ) {
+ *msgID = atoi( PQgetvalue( result, 0, 0 ) );
+ }
+ size_t msglen = atoi( PQgetvalue( result, 0, 2 ) );
/* int len = PQgetlength( result, 0, 1 ); */
const unsigned char* from =
@@ -476,11 +481,19 @@ DBMgr::GetStoredMessage( const char* const connName, int hid,
memcpy( buf, bytes, to_length );
PQfreemem( bytes );
*buflen = to_length;
+ assert( 0 == msglen || to_length == msglen );
}
PQclear( result );
return found;
}
+bool
+DBMgr::GetStoredMessage( const char* const connName, int hid,
+ unsigned char* buf, size_t* buflen, int* msgID )
+{
+ return GetNthStoredMessage( connName, hid, 0, buf, buflen, msgID );
+}
+
void
DBMgr::RemoveStoredMessage( int msgID )
{
diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h
index 196b13420..03b5975be 100644
--- a/xwords4/relay/dbmgr.h
+++ b/xwords4/relay/dbmgr.h
@@ -76,6 +76,8 @@ class DBMgr {
const unsigned char* const buf, int len );
bool GetStoredMessage( const char* const connName, int hid,
unsigned char* buf, size_t* buflen, int* msgID );
+ bool GetNthStoredMessage( const char* const connName, int hid, int nn,
+ unsigned char* buf, size_t* buflen, int* msgID );
void RemoveStoredMessage( int msgID );
private:
diff --git a/xwords4/relay/rq.c b/xwords4/relay/rq.c
index 53afeea87..1999349b5 100644
--- a/xwords4/relay/rq.c
+++ b/xwords4/relay/rq.c
@@ -54,7 +54,8 @@ usage( const char * const argv0 )
fprintf( stderr, "usage: %s \\\n", argv0 );
fprintf( stderr, "\t[-p ] # (default %d)\\\n", DEFAULT_PORT );
fprintf( stderr, "\t[-a ] # (default: %s)\\\n", DEFAULT_HOST );
- fprintf( stderr, "\t -r # list open public rooms\\\n" );
+ fprintf( stderr, "\t[-r] # list open public rooms\\\n" );
+ fprintf( stderr, "\t[-f # fetch message > stdout\\\n" );
fprintf( stderr, "\t[-l ] # language for rooms "
"(1=English default)\\\n" );
fprintf( stderr, "\t[-n ] # number of players (2 default)\\\n" );
@@ -118,7 +119,8 @@ do_rooms( int sockfd, int lang, int nPlayers )
}
static void
-do_msgs( int sockfd, const char** connNames, int nConnNames )
+write_connnames( int sockfd, char cmd,
+ const char** connNames, int nConnNames )
{
unsigned short len, netlen;
int ii;
@@ -126,7 +128,7 @@ do_msgs( int sockfd, const char** connNames, int nConnNames )
len += 1 + strlen( connNames[ii] );
}
- unsigned char hdr[] = { 0, PRX_HAS_MSGS };
+ unsigned char hdr[] = { 0, cmd };
unsigned short netNConnNames = htons( nConnNames );
netlen = sizeof(hdr) + sizeof( netNConnNames ) + len;
netlen = htons( netlen );
@@ -137,6 +139,12 @@ do_msgs( int sockfd, const char** connNames, int nConnNames )
write( sockfd, connNames[ii], strlen(connNames[ii]) );
write( sockfd, "\n", 1 );
}
+}
+
+static void
+do_msgs( int sockfd, const char** connNames, int nConnNames )
+{
+ write_connnames( sockfd, PRX_HAS_MSGS, connNames, nConnNames );
fprintf( stderr, "Waiting for response....\n" );
unsigned char reply[1024];
@@ -159,9 +167,57 @@ do_msgs( int sockfd, const char** connNames, int nConnNames )
fprintf( stdout, "%s -- %d\n", connNames[ii], count );
}
}
-
} /* do_msgs */
+static void
+do_fetch( int sockfd, const char** connNames, int nConnNames )
+{
+ assert( 1 == nConnNames );
+ write_connnames( sockfd, PRX_GET_MSGS, connNames, nConnNames );
+
+ fprintf( stderr, "Waiting for response....\n" );
+ unsigned char reply[1024];
+ int nRead = read_packet( sockfd, reply, sizeof(reply) );
+ if ( nRead > 2 ) {
+ const unsigned char* bufp = reply;
+ const unsigned char* const end = bufp + nRead;
+
+ unsigned short count;
+ memcpy( &count, bufp, sizeof( count ) );
+ bufp += sizeof( count );
+ count = ntohs( count );
+ assert( count == nConnNames );
+ fprintf( stderr, "got count: %d\n", count );
+
+ /* Now we have an array of pairs. Just
+ write em as long as it makes sense. countPerDev makes no sense as
+ other than 1 unless the UI is changed so I don't have to write to
+ STDOUT -- e.g. by passing in named pipes to correspond to each
+ deviceid provided */
+
+ while ( bufp < end ) {
+ unsigned short countPerDev;
+ memcpy( &countPerDev, bufp, sizeof( countPerDev ) );
+ bufp += sizeof( countPerDev );
+ countPerDev = ntohs( countPerDev );
+
+ while ( bufp < end && countPerDev-- > 0 ) {
+ unsigned short len;
+ memcpy( &len, bufp, sizeof( len ) );
+ len = ntohs( len ) + sizeof( len );
+ if ( bufp + len > end ) {
+ break;
+ }
+ write( STDOUT_FILENO, bufp, len );
+ bufp += len;
+ }
+ }
+ if ( bufp != end ) {
+ fprintf( stderr, "error: message not internally as expected\n" );
+ }
+ }
+}
+
static void
do_deletes( int sockfd, const char** connNames, int nConnNames )
{
@@ -217,12 +273,13 @@ main( int argc, char * const argv[] )
bool doRooms = false;
bool doMgs = false;
bool doDeletes = false;
+ bool doFetch = false;
const char* host = DEFAULT_HOST;
char const* connNames[MAX_CONN_NAMES];
int nConnNames = 0;
for ( ; ; ) {
- int opt = getopt( argc, argv, "a:d:p:rl:n:m:" );
+ int opt = getopt( argc, argv, "a:d:f:p:rl:n:m:" );
if ( opt < 0 ) {
break;
}
@@ -239,6 +296,10 @@ main( int argc, char * const argv[] )
connNames[nConnNames++] = optarg;
doDeletes = true;
break;
+ case 'f':
+ connNames[nConnNames++] = optarg;
+ doFetch = true;
+ break;
case 'l':
lang = atoi(optarg);
break;
@@ -294,6 +355,12 @@ main( int argc, char * const argv[] )
if ( doDeletes ) {
do_deletes( sockfd, connNames, nConnNames );
}
+ if ( doFetch ) {
+ if ( nConnNames != 1 ) {
+ usage( argv[0] );
+ }
+ do_fetch( sockfd, connNames, nConnNames );
+ }
close( sockfd );
diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp
index 4f3744123..16514707b 100644
--- a/xwords4/relay/tpool.cpp
+++ b/xwords4/relay/tpool.cpp
@@ -104,19 +104,19 @@ XWThreadPool::Stop()
int ii;
for ( ii = 0; ii < m_nThreads; ++ii ) {
- enqueue( 0 );
+ enqueue( 0, STYPE_UNKNOWN );
}
interrupt_poll();
}
void
-XWThreadPool::AddSocket( int socket )
+XWThreadPool::AddSocket( int socket, SockType stype )
{
logf( XW_LOGINFO, "%s(%d)", __func__, socket );
{
RWWriteLock ml( &m_activeSocketsRWLock );
- m_activeSockets.push_back( socket );
+ m_activeSockets.push_back( pair(socket, stype) );
}
interrupt_poll();
}
@@ -128,9 +128,9 @@ XWThreadPool::RemoveSocket( int socket )
{
RWWriteLock ml( &m_activeSocketsRWLock );
- vector::iterator iter = m_activeSockets.begin();
+ vector< pair >::iterator iter = m_activeSockets.begin();
while ( iter != m_activeSockets.end() ) {
- if ( *iter == socket ) {
+ if ( iter->first == socket ) {
m_activeSockets.erase( iter );
found = true;
break;
@@ -171,23 +171,27 @@ XWThreadPool::CloseSocket( int socket )
void
XWThreadPool::EnqueueKill( int socket, const char* const why )
{
- enqueue( socket, Q_KILL );
+ enqueue( socket, STYPE_UNKNOWN, Q_KILL );
}
bool
-XWThreadPool::get_process_packet( int socket )
+XWThreadPool::get_process_packet( int socket, SockType stype )
{
bool success = false;
short packetSize;
assert( sizeof(packetSize) == 2 );
- unsigned char buf[MAX_MSG_LEN];
+ unsigned char buf[MAX_MSG_LEN+1];
int nRead = read_packet( socket, buf, sizeof(buf) );
if ( nRead < 0 ) {
EnqueueKill( socket, "bad packet" );
- } else {
+ } else if ( STYPE_GAME == stype ) {
logf( XW_LOGINFO, "calling m_pFunc" );
success = (*m_pFunc)( buf, nRead, socket );
+ } else {
+ buf[nRead] = '\0';
+ handle_proxy_packet( buf, nRead, socket );
+ CloseSocket( socket );
}
return success;
} /* get_process_packet */
@@ -230,8 +234,8 @@ XWThreadPool::real_tpool_main()
pr.m_socket );
switch ( pr.m_act ) {
case Q_READ:
- if ( get_process_packet( pr.m_socket ) ) {
- AddSocket( pr.m_socket );
+ if ( get_process_packet( pr.m_socket, pr.m_type ) ) {
+ AddSocket( pr.m_socket, pr.m_type );
}
break;
case Q_KILL:
@@ -267,6 +271,7 @@ XWThreadPool::real_listener()
int nSocketsAllocd = 1;
struct pollfd* fds = (pollfd*)calloc( nSocketsAllocd, sizeof(fds[0]) );
+ SockType* stypes = (SockType*)calloc( nSocketsAllocd, sizeof(stypes[0]) );
#ifdef LOG_POLL
char* log = (char*)malloc( 4 * nSocketsAllocd );
#endif
@@ -282,31 +287,35 @@ XWThreadPool::real_listener()
if ( nSockets > nSocketsAllocd ) {
fds = (struct pollfd*)realloc( fds, nSockets * sizeof(fds[0]) );
+ stypes = (SockType*)realloc( stypes, nSockets * sizeof(stypes[0]) );
#ifdef LOG_POLL
log = (char*)realloc( log, nSockets * 4 );
#endif
nSocketsAllocd = nSockets;
}
- struct pollfd* curfd = fds;
+ int curfd = 0;
- curfd->fd = m_pipeRead;
- curfd->events = flags;
+ fds[curfd].fd = m_pipeRead;
+ fds[curfd].events = flags;
#ifdef LOG_POLL
- logLen += snprintf( log+logLen, logCapacity - logLen, "%d,", curfd->fd );
+ logLen += snprintf( log+logLen, logCapacity - logLen, "%d,",
+ fds[curfd].fd );
#endif
++curfd;
- vector::iterator iter = m_activeSockets.begin();
- while ( iter != m_activeSockets.end() ) {
- curfd->fd = *iter++;
- curfd->events = flags;
+ vector< pair >::iterator iter;
+ for ( iter = m_activeSockets.begin(); iter != m_activeSockets.end();
+ ++iter ) {
+ fds[curfd].fd = iter->first;
+ stypes[curfd] = iter->second;
+ fds[curfd].events = flags;
#ifdef LOG_POLL
if ( logCapacity > logLen ) {
logLen += snprintf( log+logLen, logCapacity - logLen, "%d,",
- curfd->fd );
+ fds[curfd].fd );
}
#endif
- assert( curfd < fds + nSockets );
+ assert( curfd < nSockets );
++curfd;
}
pthread_rwlock_unlock( &m_activeSocketsRWLock );
@@ -343,13 +352,13 @@ XWThreadPool::real_listener()
if ( nEvents > 0 ) {
--nSockets;
- curfd = &fds[1];
+ curfd = 1;
int ii;
for ( ii = 0; ii < nSockets && nEvents > 0; ++ii ) {
- if ( curfd->revents != 0 ) {
- int socket = curfd->fd;
+ if ( fds[curfd].revents != 0 ) {
+ int socket = fds[curfd].fd;
if ( !RemoveSocket( socket ) ) {
/* no further processing if it's been removed while
we've been sleeping in poll */
@@ -357,11 +366,12 @@ XWThreadPool::real_listener()
continue;
}
- if ( 0 != (curfd->revents & (POLLIN | POLLPRI)) ) {
+ if ( 0 != (fds[curfd].revents & (POLLIN | POLLPRI)) ) {
logf( XW_LOGINFO, "enqueuing %d", socket );
- enqueue( socket );
+ enqueue( socket, stypes[curfd] );
} else {
- logf( XW_LOGERROR, "odd revents: %x", curfd->revents );
+ logf( XW_LOGERROR, "odd revents: %x",
+ fds[curfd].revents );
EnqueueKill( socket, "error/hup in poll()" );
}
--nEvents;
@@ -386,10 +396,10 @@ XWThreadPool::listener_main( void* closure )
}
void
-XWThreadPool::enqueue( int socket, QAction act )
+XWThreadPool::enqueue( int socket, SockType stype, QAction act )
{
MutexLock ml( &m_queueMutex );
- QueuePr pr = { act, socket };
+ QueuePr pr = { act, socket, stype };
m_queue.push_back( pr );
logf( XW_LOGINFO, "calling pthread_cond_signal" );
diff --git a/xwords4/relay/tpool.h b/xwords4/relay/tpool.h
index 5b2e6a16e..353b32d58 100644
--- a/xwords4/relay/tpool.h
+++ b/xwords4/relay/tpool.h
@@ -35,6 +35,8 @@ using namespace std;
class XWThreadPool {
public:
+ typedef enum { STYPE_UNKNOWN, STYPE_GAME, STYPE_PROXY } SockType;
+
static XWThreadPool* GetTPool();
typedef bool (*packet_func)( unsigned char* buf, int bufLen, int socket );
typedef void (*kill_func)( int socket );
@@ -46,7 +48,7 @@ class XWThreadPool {
void Stop();
/* Add to set being listened on */
- void AddSocket( int socket );
+ void AddSocket( int socket, SockType stype );
/* remove from tpool altogether, and close */
void CloseSocket( int socket );
@@ -54,17 +56,18 @@ class XWThreadPool {
private:
typedef enum { Q_READ, Q_KILL } QAction;
- typedef struct { QAction m_act; int m_socket; } QueuePr;
+ typedef struct { QAction m_act; int m_socket; SockType m_type; } QueuePr;
/* Remove from set being listened on */
bool RemoveSocket( int socket );
void enqueue( int socket, QAction act = Q_READ );
+ void enqueue( int socket, SockType stype, QAction act = Q_READ );
void release_socket_locked( int socket );
void grab_elem_locked( QueuePr* qpp );
void print_in_use( void );
- bool get_process_packet( int socket );
+ bool get_process_packet( int socket, SockType stype );
void interrupt_poll();
void* real_tpool_main();
@@ -74,7 +77,7 @@ class XWThreadPool {
static void* listener_main( void* closure );
/* Sockets main thread listens on */
- vector m_activeSockets;
+ vector< pair >m_activeSockets;
pthread_rwlock_t m_activeSocketsRWLock;
/* Sockets waiting for a thread to read 'em */
diff --git a/xwords4/relay/xwrelay.conf_tmplate b/xwords4/relay/xwrelay.conf_tmplate
index 5c62759a2..3daa1b19d 100644
--- a/xwords4/relay/xwrelay.conf_tmplate
+++ b/xwords4/relay/xwrelay.conf_tmplate
@@ -12,8 +12,10 @@ HEARTBEAT=60
# Default -- if not set -- is an infinite timeout.
# ALLCONN=300
-# How many worker threads in the thread pool? Default is five.
-NTHREADS=3
+# How many worker threads in the thread pool? Default is five. Let's
+# keep this at 1 until the race condition is fixed. All interaction
+# with crefs should be from this one thread, including proxy stuff.
+NTHREADS=1
# How many seconds to wait for device to ack new connName
DEVACK=3
diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp
index 9181675fa..7da9f14e2 100644
--- a/xwords4/relay/xwrelay.cpp
+++ b/xwords4/relay/xwrelay.cpp
@@ -77,8 +77,6 @@
#include "dbmgr.h"
static int s_nSpawns = 0;
-#define MAX_PROXY_LEN 64
-#define MAX_PROXY_COUNT 48
void
logf( XW_LogLevel level, const char* format, ... )
@@ -193,7 +191,8 @@ parseRelayID( const char* const in, char* buf, HostID* hid )
}
static bool
-getNetShort( unsigned char** bufpp, unsigned char* end, unsigned short* out )
+getNetShort( unsigned char** bufpp, const unsigned char* end,
+ unsigned short* out )
{
bool ok = *bufpp + 2 <= end;
if ( ok ) {
@@ -206,7 +205,8 @@ getNetShort( unsigned char** bufpp, unsigned char* end, unsigned short* out )
} /* getNetShort */
static bool
-getNetByte( unsigned char** bufpp, unsigned char* end, unsigned char* out )
+getNetByte( unsigned char** bufpp, const unsigned char* end,
+ unsigned char* out )
{
bool ok = *bufpp < end;
if ( ok ) {
@@ -683,7 +683,7 @@ read_packet( int sock, unsigned char* buf, int buflen )
nread = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL );
if ( nread == sizeof(msgLen) ) {
msgLen = ntohs( msgLen );
- if ( msgLen <= buflen ) {
+ if ( msgLen < buflen ) {
nread = recv( sock, buf, msgLen, MSG_WAITALL );
if ( nread == msgLen ) {
result = nread;
@@ -693,16 +693,82 @@ read_packet( int sock, unsigned char* buf, int buflen )
return result;
}
-static void*
-handle_proxy_tproc( void* closure )
+static void
+pushShort( vector& out, unsigned short num )
{
- blockSignals();
- int sock = (int)closure;
+ num = htons( num );
+ out.insert( out.end(), (unsigned char*)&num, ((unsigned char*)&num) + 2 );
+}
- unsigned char buf[MAX_PROXY_MSGLEN];
- int len = read_packet( sock, buf, sizeof(buf)-1 );
+static void
+pushMsgs( vector& out, DBMgr* dbmgr, const char* connName,
+ HostID hid, int msgCount )
+{
+ int ii;
+ for ( ii = 0; ii < msgCount; ++ii ) {
+ unsigned char buf[1024];
+ size_t buflen = sizeof(buf);
+ if ( !dbmgr->GetNthStoredMessage( connName, hid, ii, buf,
+ &buflen, NULL ) ) {
+ logf( XW_LOGERROR, "%s: %dth message not there", __func__, ii );
+ break;
+ }
+ pushShort( out, buflen );
+ out.insert( out.end(), buf, buf + buflen );
+ }
+}
+
+static void
+handleMsgsMsg( int sock, bool sendFull,
+ unsigned char* bufp, const unsigned char* end )
+{
+ unsigned short nameCount;
+ if ( getNetShort( &bufp, end, &nameCount ) ) {
+ char* in = (char*)bufp;
+ DBMgr* dbmgr = DBMgr::Get();
+ unsigned short count;
+ /* This is wrong now */
+ /* reply format: PRX_GET_MSGS case: []*
+ * PRX_HAS_MSGS case: *
+ */
+ vector out(4); /* space for len and n_msgs */
+ assert( out.size() == 4 );
+
+ char* saveptr;
+ for ( count = 0; ; ++count ) {
+ char* name = strtok_r( in, "\n", &saveptr );
+ if ( NULL == name ) {
+ break;
+ }
+ HostID hid;
+ char connName[MAX_CONNNAME_LEN+1];
+ if ( !parseRelayID( name, connName, &hid ) ) {
+ break;
+ }
+
+ /* For each relayID, write the number of messages and then each
+ message (in the getmsg case) */
+ int msgCount = dbmgr->PendingMsgCount( connName, hid );
+ pushShort( out, msgCount );
+ if ( sendFull ) {
+ pushMsgs( out, dbmgr, connName, hid, msgCount );
+ }
+
+ in = NULL;
+ }
+
+ unsigned short tmp = htons( out.size() - sizeof(tmp) );
+ memcpy( &out[0], &tmp, sizeof(tmp) );
+ tmp = htons( count );
+ memcpy( &out[2], &tmp, sizeof(tmp) );
+ write( sock, &out[0], out.size() );
+ }
+}
+
+void
+handle_proxy_packet( unsigned char* buf, int len, int sock )
+{
if ( len > 0 ) {
- buf[len] = '\0'; /* so can use strtok */
unsigned char* bufp = buf;
unsigned char* end = bufp + len;
if ( (0 == *bufp++) ) { /* protocol */
@@ -729,42 +795,12 @@ handle_proxy_tproc( void* closure )
}
break;
case PRX_HAS_MSGS:
+ case PRX_GET_MSGS:
if ( len >= 2 ) {
- unsigned short nameCount;
- if ( getNetShort( &bufp, end, &nameCount ) ) {
- char* in = (char*)bufp;
- char* saveptr;
- vector ids;
- for ( ; ; ) {
- char* name = strtok_r( in, "\n", &saveptr );
- if ( NULL == name ) {
- break;
- }
- HostID hid;
- char connName[MAX_CONNNAME_LEN+1];
- if ( parseRelayID( name, connName, &hid ) ) {
- ids.push_back( DBMgr::Get()->
- PendingMsgCount( connName, hid ) );
- }
- in = NULL;
- }
-
- unsigned short len =
- (ids.size() * sizeof(unsigned short))
- + sizeof( unsigned short );
- len = htons( len );
- write( sock, &len, sizeof(len) );
- len = htons( nameCount );
- write( sock, &len, sizeof(len) );
- vector::const_iterator iter;
- for ( iter = ids.begin(); iter != ids.end(); ++iter ) {
- unsigned short num = *iter;
- num = htons( num );
- write( sock, &num, sizeof(num) );
- }
- }
+ handleMsgsMsg( sock, PRX_GET_MSGS == cmd, bufp, end );
}
- break;
+ break; /* PRX_HAS_MSGS */
+
case PRX_DEVICE_GONE:
logf( XW_LOGINFO, "%s: got PRX_DEVICE_GONE", __func__ );
if ( len >= 2 ) {
@@ -799,24 +835,11 @@ handle_proxy_tproc( void* closure )
}
len = 0; /* return a 0-length message */
write( sock, &len, sizeof(len) );
- break;
+ break; /* PRX_DEVICE_GONE */
}
}
}
- sleep( 2 );
- close( sock );
- return NULL;
-} /* handle_proxy_tproc */
-
-static void
-handle_proxy_connect( int sock )
-{
- pthread_t thread;
- if ( 0 == pthread_create( &thread, NULL, handle_proxy_tproc,
- (void*)sock ) ) {
- pthread_detach( thread );
- }
-} /* handle_proxy_connect */
+} /* handle_proxy_packet */
int
main( int argc, char** argv )
@@ -839,17 +862,13 @@ main( int argc, char** argv )
/* Verify sizes here... */
assert( sizeof(CookieID) == 2 );
-
/* Read options. Options trump config file values when they conflict, but
the name of the config file is an option so we have to get that
first. */
for ( ; ; ) {
- int opt = getopt(argc, argv, "h?c:p:n:i:f:l:t:"
-#ifdef DO_HTTP
- "w:s:"
-#endif
+ int opt = getopt(argc, argv, "h?c:p:n:i:f:l:t:s:w:"
"DF" );
if ( opt == -1 ) {
@@ -870,6 +889,11 @@ main( int argc, char** argv )
case 's':
cssFile = optarg;
break;
+#else
+ case 'w':
+ case 's':
+ fprintf( stderr, "option -%c disabled and ignored\n", opt );
+ break;
#endif
case 'D':
doDaemon = false;
@@ -1120,15 +1144,14 @@ main( int argc, char** argv )
int newSock = accept( listener, (sockaddr*)&newaddr,
&siz );
- if ( perGame ) {
- logf( XW_LOGINFO,
- "accepting connection from %s on socket %d",
- inet_ntoa(newaddr.sin_addr), newSock );
+ logf( XW_LOGINFO,
+ "accepting connection from %s on socket %d",
+ inet_ntoa(newaddr.sin_addr), newSock );
+
+ tPool->AddSocket( newSock,
+ perGame ? XWThreadPool::STYPE_GAME
+ : XWThreadPool::STYPE_PROXY );
- tPool->AddSocket( newSock );
- } else {
- handle_proxy_connect( newSock );
- }
--retval;
}
}
diff --git a/xwords4/relay/xwrelay.h b/xwords4/relay/xwrelay.h
index fa2c45f7e..1b726b04d 100644
--- a/xwords4/relay/xwrelay.h
+++ b/xwords4/relay/xwrelay.h
@@ -154,6 +154,7 @@ enum { PRX_NONE /* 0 is an illegal value */
,PRX_PUB_ROOMS /* list all public rooms for lang/nPlayers */
,PRX_HAS_MSGS /* return message counts for connName/devid array */
,PRX_DEVICE_GONE /* return message counts for connName/devid array */
+ ,PRX_GET_MSGS /* return full messages for connName/devid array */
}
#ifndef CANT_DO_TYPEDEF
XWPRXYCMD
diff --git a/xwords4/relay/xwrelay.sh b/xwords4/relay/xwrelay.sh
index 48644b47c..1116ffda4 100755
--- a/xwords4/relay/xwrelay.sh
+++ b/xwords4/relay/xwrelay.sh
@@ -54,6 +54,7 @@ id SERIAL
,hid INTEGER
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
,msg BYTEA
+,msglen INTEGER
,UNIQUE ( connName, hid, msg )
);
EOF
diff --git a/xwords4/relay/xwrelay_priv.h b/xwords4/relay/xwrelay_priv.h
index 44f989967..5226609a2 100644
--- a/xwords4/relay/xwrelay_priv.h
+++ b/xwords4/relay/xwrelay_priv.h
@@ -30,6 +30,7 @@ int GetNSpawns(void);
int make_socket( unsigned long addr, unsigned short port );
int read_packet( int sock, unsigned char* buf, int buflen );
+void handle_proxy_packet( unsigned char* buf, int bufLen, int socket );
const char* cmdToStr( XWRELAY_Cmd cmd );