From 6c7cf1fa97d1d8617b05d00ccd3cca3419ffdfc6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 23 May 2013 06:48:40 -0700 Subject: [PATCH 001/126] fix bug where two quick taps on a game will launch two copies (or crash a slower device) by tracking whether an open's in progress --- .../XWords4/src/org/eehouse/android/xw4/GamesList.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index ff1677320..4132e8132 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -101,6 +101,7 @@ public class GamesList extends XWExpandableListActivity private String m_nameField; private NetLaunchInfo m_netLaunchInfo; private GameNamer m_namer; + private boolean m_gameLaunched = false; @Override protected Dialog onCreateDialog( int id ) @@ -462,6 +463,7 @@ public class GamesList extends XWExpandableListActivity super.onWindowFocusChanged( hasFocus ); if ( hasFocus ) { updateField(); + m_gameLaunched = false; } } @@ -1065,7 +1067,10 @@ public class GamesList extends XWExpandableListActivity private void launchGame( long rowid, boolean invited ) { - GameUtils.launchGame( this, rowid, invited ); + if ( !m_gameLaunched ) { + m_gameLaunched = true; + GameUtils.launchGame( this, rowid, invited ); + } } private void launchGame( long rowid ) From f4c18a78acb3831bb95c0e84bf9de68b65fff445 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 23 May 2013 07:51:03 -0700 Subject: [PATCH 002/126] clear game-launched flag in onNewIntent so that one game can replace another via a Notification- or other-sourced Intent --- .../org/eehouse/android/xw4/GamesList.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 4132e8132..464307ab6 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -385,6 +385,7 @@ public class GamesList extends XWExpandableListActivity protected void onNewIntent( Intent intent ) { super.onNewIntent( intent ); + m_gameLaunched = false; Assert.assertNotNull( intent ); invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) ); startFirstHasDict( intent ); @@ -487,21 +488,23 @@ public class GamesList extends XWExpandableListActivity // We need a way to let the user get back to the basic-config // dialog in case it was dismissed. That way it to check for // an empty room name. - if ( summary.conType == CommsAddrRec.CommsConnType.COMMS_CONN_RELAY - && summary.roomName.length() == 0 ) { - // If it's unconfigured and of the type RelayGameActivity - // can handle send it there, otherwise use the full-on - // config. - Class clazz; - if ( RelayGameActivity.isSimpleGame( summary ) ) { - clazz = RelayGameActivity.class; + if ( !m_gameLaunched ) { + if ( summary.conType == CommsAddrRec.CommsConnType.COMMS_CONN_RELAY + && summary.roomName.length() == 0 ) { + // If it's unconfigured and of the type RelayGameActivity + // can handle send it there, otherwise use the full-on + // config. + Class clazz; + if ( RelayGameActivity.isSimpleGame( summary ) ) { + clazz = RelayGameActivity.class; + } else { + clazz = GameConfig.class; + } + GameUtils.doConfig( this, rowid, clazz ); } else { - clazz = GameConfig.class; - } - GameUtils.doConfig( this, rowid, clazz ); - } else { - if ( checkWarnNoDict( rowid ) ) { - launchGame( rowid ); + if ( checkWarnNoDict( rowid ) ) { + launchGame( rowid ); + } } } } From a1b6641067390fe913e62111cbc69ca265bee5c7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 24 May 2013 21:17:22 -0700 Subject: [PATCH 003/126] catch and ignore exception from malformed user-provided url --- .../eehouse/android/xw4/UpdateCheckReceiver.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java index 932f699db..608f4cac1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/UpdateCheckReceiver.java @@ -165,7 +165,14 @@ public class UpdateCheckReceiver extends BroadcastReceiver { String url = String.format( "%s/%s", XWPrefs.getDefaultUpdateUrl( context ), proc ); - return new HttpPost( url ); + HttpPost result; + try { + result = new HttpPost( url ); + } catch ( IllegalArgumentException iae ) { + DbgUtils.loge( iae ); + result = null; + } + return result; } private static String runPost( HttpPost post, JSONObject params ) @@ -244,7 +251,10 @@ public class UpdateCheckReceiver extends BroadcastReceiver { @Override protected String doInBackground( Void... unused ) { HttpPost post = makePost( m_context, "getUpdates" ); - String json = runPost( post, m_params ); + String json = null; + if ( null != post ) { + json = runPost( post, m_params ); + } return json; } From ffa983db9401e025a70a6dc7e464704a345a9fa3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 24 May 2013 21:19:20 -0700 Subject: [PATCH 004/126] use pipe to quit from signal handler since calling g_main_loop_quit() doesn't work with more recent glib versions (and has always been a bad idea) --- xwords4/linux/cursesmain.c | 15 ++++++++++++++- xwords4/linux/cursesmain.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index bd442f884..a0693a6d4 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -960,7 +960,9 @@ SIGWINCH_handler( int signal ) static void SIGINTTERM_handler( int XP_UNUSED(signal) ) { - (void)handleQuit( &g_globals ); + if ( 1 != write( g_globals.quitpipe[1], "!", 1 ) ) { + XP_ASSERT(0); + } } static void @@ -1113,6 +1115,14 @@ curses_socket_changed( void* closure, int oldSock, int newSock, } /* curses_socket_changed */ #ifdef USE_GLIBLOOP +static gboolean +handle_quitwrite( GIOChannel* XP_UNUSED(source), GIOCondition XP_UNUSED(condition), gpointer data ) +{ + CursesAppGlobals* globals = (CursesAppGlobals*)data; + handleQuit( globals ); + return TRUE; +} + static gboolean fire_acceptor( GIOChannel* source, GIOCondition condition, gpointer data ) { @@ -1735,6 +1745,9 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) #ifdef USE_GLIBLOOP cursesListenOnSocket( &g_globals, 0, handle_stdin ); setOneSecondTimer( &g_globals.cGlobals ); + int piperesult = pipe( g_globals.quitpipe ); + XP_ASSERT( piperesult == 0 ); + cursesListenOnSocket( &g_globals, g_globals.quitpipe[0], handle_quitwrite ); #else cursesListenOnSocket( &g_globals, 0 ); /* stdin */ diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index 74c5da17b..f9dd34cd6 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -86,6 +86,7 @@ struct CursesAppGlobals { #ifdef USE_GLIBLOOP GMainLoop* loop; GList* sources; + int quitpipe[2]; #else XP_Bool timeToExit; short fdCount; From eccd31472f22d4f847ff5af619e25cfb0c2e3ff3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 27 May 2013 16:17:58 -0700 Subject: [PATCH 005/126] fix crash when udp not in use --- xwords4/relay/xwrelay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index a1dddaa79..41e4e635b 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -1948,7 +1948,7 @@ main( int argc, char** argv ) // run_ctrl_thread( g_control ); --retval; } - if ( FD_ISSET( g_udpsock, &rfds ) ) { + if ( -1 != g_udpsock && FD_ISSET( g_udpsock, &rfds ) ) { // This will need to be done in a separate thread, or pushed // to the existing thread pool handle_udp_packet( g_udpsock ); From 72a78100b1c0df7919df19149b0d51a1154e1c56 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 27 May 2013 16:18:11 -0700 Subject: [PATCH 006/126] fix compile error in non-debug version --- xwords4/linux/cursesmain.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index a0693a6d4..a2e98c97b 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1745,7 +1745,10 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) #ifdef USE_GLIBLOOP cursesListenOnSocket( &g_globals, 0, handle_stdin ); setOneSecondTimer( &g_globals.cGlobals ); - int piperesult = pipe( g_globals.quitpipe ); +# ifdef DEBUG + int piperesult = +# endif + pipe( g_globals.quitpipe ); XP_ASSERT( piperesult == 0 ); cursesListenOnSocket( &g_globals, g_globals.quitpipe[0], handle_quitwrite ); #else From 8b888b8d967e90c82844ba69fdefd324f2b6a0c3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 29 May 2013 06:36:54 -0700 Subject: [PATCH 007/126] fix compile errors due to stricter compiler post debian upgrade --- xwords4/relay/lstnrmgr.cpp | 1 + xwords4/relay/rq.c | 10 +++++----- xwords4/relay/udpack.cpp | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/xwords4/relay/lstnrmgr.cpp b/xwords4/relay/lstnrmgr.cpp index d01118413..e38bc65bf 100644 --- a/xwords4/relay/lstnrmgr.cpp +++ b/xwords4/relay/lstnrmgr.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/xwords4/relay/rq.c b/xwords4/relay/rq.c index bd33b8bb6..617e9992a 100644 --- a/xwords4/relay/rq.c +++ b/xwords4/relay/rq.c @@ -144,8 +144,8 @@ do_rooms( int sockfd, int lang, int nPlayers ) { unsigned char msg[] = { 0, /* protocol */ PRX_PUB_ROOMS, - lang, - nPlayers }; + (unsigned char)lang, + (unsigned char)nPlayers }; unsigned short len = htons( sizeof(msg) ); write( sockfd, &len, sizeof(len) ); write( sockfd, msg, sizeof(msg) ); @@ -184,7 +184,7 @@ write_connnames( int sockfd, char cmd, len += 1 + strlen( connNames[ii] ); } - unsigned char hdr[] = { 0, cmd }; + unsigned char hdr[] = { 0, (unsigned char)cmd }; unsigned short netNConnNames = htons( nConnNames ); netlen = sizeof(hdr) + sizeof( netNConnNames ) + len; netlen = htons( netlen ); @@ -277,6 +277,7 @@ do_fetch( int sockfd, const char** connNames, int nConnNames, unsigned char reply[1024]; int nRead = read_packet( sockfd, reply, sizeof(reply) ); if ( nRead > 2 ) { + int ii; const unsigned char* bufp = reply; const unsigned char* const end = bufp + nRead; @@ -292,7 +293,7 @@ do_fetch( int sockfd, const char** connNames, int nConnNames, STDOUT -- e.g. by passing in named pipes to correspond to each deviceid provided */ - for ( int ii = 0; ii < count && bufp < end; ++ii ) { + for ( ii = 0; ii < count && bufp < end; ++ii ) { int fd = STDOUT_FILENO; int nbsfd = -1; unsigned short countPerDev; @@ -350,7 +351,6 @@ do_fetch( int sockfd, const char** connNames, int nConnNames, nwritten = write( fd, &len, sizeof(len) ); assert( nwritten == sizeof(len) ); - int ii; for ( ii = 0; -1 != nbsfd; ++ii ) { short len; ssize_t nRead = read( nbsfd, &len, sizeof(len) ); diff --git a/xwords4/relay/udpack.cpp b/xwords4/relay/udpack.cpp index 75762b829..5e5a0b97a 100644 --- a/xwords4/relay/udpack.cpp +++ b/xwords4/relay/udpack.cpp @@ -1,4 +1,4 @@ -/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */ +/* -*- compile-command: "make -j3"; -*- */ /* * Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved. * @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include "udpack.h" #include "mlock.h" From 8e8b4755b3c69703bcffa71aeb8ea46c9af796c0 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 30 May 2013 06:48:29 -0700 Subject: [PATCH 008/126] bring in changes from gtk_multigame to remove assertion fail when connname gets changed by relay due to failure to ack it (which happens when a large number of games is being run in a test.) --- xwords4/common/comms.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 4e7bcb387..01f597206 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1180,11 +1180,16 @@ gameID( const CommsCtxt* comms ) if ( 0 == gameID ) { gameID = comms->util->gameInfo->gameID; } - XP_ASSERT( 0 == comms->connID - || (comms->connID & 0xFFFF) - == (comms->util->gameInfo->gameID & 0xFFFF) ); + /* 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. */ + 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; + } + return gameID; } @@ -1375,8 +1380,11 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream, { XP_UCHAR connName[MAX_CONNNAME_LEN+1]; stringFromStreamHere( stream, connName, sizeof(connName) ); - XP_ASSERT( comms->r.connName[0] == '\0' - || 0 == XP_STRCMP( comms->r.connName, connName ) ); + if ( comms->r.connName[0] != '\0' + && 0 != XP_STRCMP( comms->r.connName, connName ) ) { + XP_LOGF( "%s: we're replacing connNames: %s overwritten by %s", + __func__, comms->r.connName, connName ); + } XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) ); XP_LOGF( "%s: connName: \"%s\"", __func__, connName ); } @@ -1443,8 +1451,11 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) { XP_UCHAR connName[MAX_CONNNAME_LEN+1]; stringFromStreamHere( stream, connName, sizeof(connName) ); - XP_ASSERT( comms->r.connName[0] == '\0' - || 0 == XP_STRCMP( comms->r.connName, connName ) ); + if ( comms->r.connName[0] != '\0' + && 0 != XP_STRCMP( comms->r.connName, connName ) ) { + XP_LOGF( "%s: we're replacing connNames: %s overwritten by %s", + __func__, comms->r.connName, connName ); + } XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) ); XP_LOGF( "%s: connName: \"%s\"", __func__, connName ); @@ -2172,7 +2183,7 @@ msg_to_stream( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID, case XWRELAY_MSG_TORELAY_NOCONN: stream_putU8( stream, comms->r.myHostID ); stream_putU8( stream, destID ); - XP_LOGF( "%s: wrote ids %d, %d", __func__, + XP_LOGF( "%s: wrote ids src %d, dest %d", __func__, comms->r.myHostID, destID ); if ( data != NULL && datalen > 0 ) { stream_putBytes( stream, data, datalen ); From d41c1a4ca89ec06f91cefc5f10e381331bba19c2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 30 May 2013 07:17:13 -0700 Subject: [PATCH 009/126] add new param so pending text can be drawn differently when it's not the player's turn. --- xwords4/android/XWords4/jni/drawwrapper.c | 2 +- xwords4/common/draw.h | 5 +++-- xwords4/common/tray.c | 3 ++- xwords4/linux/cursesdraw.c | 1 + xwords4/linux/gtkdraw.c | 18 +++++++----------- xwords4/linux/gtkmain.h | 1 + 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/xwords4/android/XWords4/jni/drawwrapper.c b/xwords4/android/XWords4/jni/drawwrapper.c index e78e3e34b..4da716a0e 100644 --- a/xwords4/android/XWords4/jni/drawwrapper.c +++ b/xwords4/android/XWords4/jni/drawwrapper.c @@ -486,7 +486,7 @@ and_draw_drawTrayDivider( DrawCtx* dctx, const XP_Rect* rect, CellFlags flags ) static void and_draw_score_pendingScore( DrawCtx* dctx, const XP_Rect* rect, XP_S16 score, XP_U16 playerNum, - CellFlags flags ) + XP_S16 curTurn, CellFlags flags ) { DRAW_CBK_HEADER( "score_pendingScore", "(Landroid/graphics/Rect;III)V" ); diff --git a/xwords4/common/draw.h b/xwords4/common/draw.h index 8179bf7ca..4e24d7019 100644 --- a/xwords4/common/draw.h +++ b/xwords4/common/draw.h @@ -165,6 +165,7 @@ typedef struct DrawCtxVTable { const XP_Rect* rect, XP_S16 score, XP_U16 playerNum, + XP_S16 curTurn, CellFlags flags ); void DRAW_VTABLE_NAME(drawTimer) ( DrawCtx* dctx, const XP_Rect* rect, @@ -295,8 +296,8 @@ struct DrawCtx { # define draw_score_drawPlayer(dc, ri, ro, gp, dsi) \ CALL_DRAW_NAME4(score_drawPlayer,(dc),(ri),(ro),(gp),(dsi)) #endif -#define draw_score_pendingScore(dc, r, s, p, f ) \ - CALL_DRAW_NAME4(score_pendingScore,(dc), (r), (s), (p), (f)) +#define draw_score_pendingScore(dc, r, s, p, t, f ) \ + CALL_DRAW_NAME5(score_pendingScore,(dc), (r), (s), (p), (t), (f)) #define draw_drawTimer( dc, r, plyr, sec ) \ CALL_DRAW_NAME3(drawTimer,(dc),(r),(plyr),(sec)) #define draw_drawCell( dc, rect, txt, bmap, t, v,o, bon, hi, f ) \ diff --git a/xwords4/common/tray.c b/xwords4/common/tray.c index 07b5cc70e..b1f11db84 100644 --- a/xwords4/common/tray.c +++ b/xwords4/common/tray.c @@ -324,11 +324,12 @@ drawPendingScore( BoardCtxt* board, XP_S16 turnScore, XP_Bool hasCursor ) /* Draw the pending score down in the last tray's rect */ if ( countTilesToShow( board ) < MAX_TRAY_TILES ) { XP_U16 selPlayer = board->selPlayer; + XP_S16 curTurn = server_getCurrentTurn( board->server ); XP_Rect lastTileR; figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR ); draw_score_pendingScore( board->draw, &lastTileR, turnScore, - selPlayer, + selPlayer, curTurn, hasCursor?CELL_ISCURSOR:CELL_NONE ); } } /* drawPendingScore */ diff --git a/xwords4/linux/cursesdraw.c b/xwords4/linux/cursesdraw.c index 9e2d9ea3c..cac9b536d 100644 --- a/xwords4/linux/cursesdraw.c +++ b/xwords4/linux/cursesdraw.c @@ -283,6 +283,7 @@ curses_draw_score_drawPlayer( DrawCtx* p_dctx, const XP_Rect* rInner, static void curses_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, XP_S16 score, XP_U16 XP_UNUSED(playerNum), + XP_S16 XP_UNUSED(curTurn), CellFlags XP_UNUSED(flags) ) { CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx; diff --git a/xwords4/linux/gtkdraw.c b/xwords4/linux/gtkdraw.c index 7aa67c379..edcaa7e92 100644 --- a/xwords4/linux/gtkdraw.c +++ b/xwords4/linux/gtkdraw.c @@ -1123,8 +1123,8 @@ gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* bounds, static void gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, - XP_S16 score, XP_U16 XP_UNUSED(playerNum), - CellFlags flags ) + XP_S16 score, XP_U16 playerNum, + XP_S16 curTurn, CellFlags flags ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_UCHAR buf[5]; @@ -1132,6 +1132,7 @@ gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, XP_Rect localR; GdkColor* cursor = ((flags & CELL_ISCURSOR) != 0) ? &dctx->cursor : NULL; + GdkColor* txtColor; if ( score >= 0 ) { XP_SNPRINTF( buf, VSIZE(buf), "%.3d", score ); @@ -1151,12 +1152,11 @@ gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, } ht = localR.height >> 2; + txtColor = (playerNum == curTurn) ? &dctx->black : &dctx->grey; draw_string_at( dctx, NULL, "Pts:", ht, - &localR, XP_GTK_JUST_TOPLEFT, - &dctx->black, cursor ); + &localR, XP_GTK_JUST_TOPLEFT, txtColor, cursor ); draw_string_at( dctx, NULL, buf, ht, - &localR, XP_GTK_JUST_BOTTOMRIGHT, - &dctx->black, cursor ); + &localR, XP_GTK_JUST_BOTTOMRIGHT, txtColor, cursor ); } /* gtk_draw_score_pendingScore */ @@ -1374,11 +1374,6 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals ) dctx->drawing_area = drawing_area; dctx->globals = globals; - map = gdk_colormap_get_system(); - - allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 ); - allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF ); - { // GdkWindow *window = NULL; /* if ( GTK_WIDGET_FLAGS(GTK_WIDGET(drawing_area)) & GTK_NO_WINDOW ) { */ @@ -1405,6 +1400,7 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals ) map = gdk_colormap_get_system(); allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 ); + allocAndSet( map, &dctx->grey, 0x7FFF, 0x7FFF, 0x7FFF ); allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF ); allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF ); diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h index ca06ea8b3..d663a127a 100644 --- a/xwords4/linux/gtkmain.h +++ b/xwords4/linux/gtkmain.h @@ -55,6 +55,7 @@ typedef struct GtkDrawCtx { GdkColor black; GdkColor white; + GdkColor grey; GdkColor red; /* for pending tiles */ GdkColor tileBack; /* for pending tiles */ GdkColor cursor; From 9d40930fff5b1fee0325dfbfe071a3e5e5579704 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 31 May 2013 07:40:42 -0700 Subject: [PATCH 010/126] pass new param into java, and when it's not the player's turn draw pending points with his color at half-intensity. --- xwords4/android/XWords4/jni/drawwrapper.c | 4 ++-- .../org/eehouse/android/xw4/BoardView.java | 22 +++++++++++++++++-- .../org/eehouse/android/xw4/jni/DrawCtx.java | 3 ++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/xwords4/android/XWords4/jni/drawwrapper.c b/xwords4/android/XWords4/jni/drawwrapper.c index 4da716a0e..7f5874cde 100644 --- a/xwords4/android/XWords4/jni/drawwrapper.c +++ b/xwords4/android/XWords4/jni/drawwrapper.c @@ -488,12 +488,12 @@ and_draw_score_pendingScore( DrawCtx* dctx, const XP_Rect* rect, XP_S16 score, XP_U16 playerNum, XP_S16 curTurn, CellFlags flags ) { - DRAW_CBK_HEADER( "score_pendingScore", "(Landroid/graphics/Rect;III)V" ); + DRAW_CBK_HEADER( "score_pendingScore", "(Landroid/graphics/Rect;IIII)V" ); jobject jrect = makeJRect( draw, JCACHE_RECT0, rect ); (*env)->CallVoidMethod( env, draw->jdraw, mid, - jrect, score, playerNum, flags ); + jrect, score, playerNum, curTurn, flags ); } static void diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index 773f7f789..d428ed13a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -888,14 +888,19 @@ public class BoardView extends View implements DrawCtx, BoardHandler, } public void score_pendingScore( Rect rect, int score, int playerNum, - int flags ) + int curTurn, int flags ) { String text = score >= 0? String.format( "%d", score ) : "??"; int otherIndx = (0 == (flags & CELL_ISCURSOR)) ? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS; ++rect.top; fillRectOther( rect, otherIndx ); - m_fillPaint.setColor( m_playerColors[playerNum] ); + + int playerColor = m_playerColors[playerNum]; + if ( playerNum != curTurn ) { + playerColor = dimColor( playerColor ); + } + m_fillPaint.setColor( playerColor ); rect.bottom -= rect.height() / 2; drawCentered( text, rect, null ); @@ -1258,4 +1263,17 @@ public class BoardView extends View implements DrawCtx, BoardHandler, return zoomDir; } + private int dimColor( int playerColor ) + { + int newColor = 0; + for ( int ii = 0; ii < 4; ++ii ) { + int byt = (playerColor >> 24) & 0xFF; + playerColor <<= 8; + byt += (0xFF - byt) / 2; + newColor <<= 8; + newColor |= byt; + } + return newColor; + } + } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/DrawCtx.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/DrawCtx.java index f5a535ef9..71a321a0a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/DrawCtx.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/DrawCtx.java @@ -68,7 +68,8 @@ public interface DrawCtx { int flags ); void drawTileBack( Rect rect, int flags ); void drawTrayDivider( Rect rect, int flags ); - void score_pendingScore( Rect rect, int score, int playerNum, int flags ); + void score_pendingScore( Rect rect, int score, int playerNum, int curTurn, + int flags ); public static final int BONUS_NONE = 0; public static final int BONUS_DOUBLE_LETTER = 1; From c32df1a48702ebee8eb32395c053ac952b0c65b6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 1 Jun 2013 08:10:03 -0700 Subject: [PATCH 011/126] use alpha to dim pending score rather than mucking with the rgb values --- .../src/org/eehouse/android/xw4/BoardView.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index d428ed13a..4988ff609 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -50,6 +50,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, private static Bitmap s_bitmap; // the board private static final int IN_TRADE_ALPHA = 0x3FFFFFFF; + private static final int NOT_TURN_ALPHA = 0x3FFFFFFF; private static final int PINCH_THRESHOLD = 40; private static final int SCORE_HT_DROP = 2; private static final boolean DEBUG_DRAWFRAMES = false; @@ -898,7 +899,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler, int playerColor = m_playerColors[playerNum]; if ( playerNum != curTurn ) { - playerColor = dimColor( playerColor ); + playerColor &= NOT_TURN_ALPHA; } m_fillPaint.setColor( playerColor ); @@ -1263,17 +1264,4 @@ public class BoardView extends View implements DrawCtx, BoardHandler, return zoomDir; } - private int dimColor( int playerColor ) - { - int newColor = 0; - for ( int ii = 0; ii < 4; ++ii ) { - int byt = (playerColor >> 24) & 0xFF; - playerColor <<= 8; - byt += (0xFF - byt) / 2; - newColor <<= 8; - newColor |= byt; - } - return newColor; - } - } From bb130a9b9f9961b8286d5800f52eb009afe13d7d Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 4 Jun 2013 07:29:40 -0700 Subject: [PATCH 012/126] new feature: long-click on wordlist button in BoardActivity brings up a popup of all wordlists so you can browse one that's not in use, e.g. to see if a word's legal in a larger wordlist. Uses PopupMenu class that was introduced in API 11, so 11's the target now and the interface trick is used to prevent crashing on older devices (which get a "needs newer Android" alert.) --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- xwords4/android/XWords4/project.properties | 2 +- .../XWords4/res/values/common_rsrc.xml | 1 + .../android/XWords4/res/values/strings.xml | 8 ++- .../eehouse/android/xw4/BoardActivity.java | 14 +++++ .../eehouse/android/xw4/DictsActivity.java | 57 +++++++++++++++++++ .../src/org/eehouse/android/xw4/Toolbar.java | 28 ++++++++- 7 files changed, 108 insertions(+), 4 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index a11eab419..682161ac9 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -26,7 +26,7 @@ android:versionName="@string/app_version" > - + diff --git a/xwords4/android/XWords4/project.properties b/xwords4/android/XWords4/project.properties index c1fd41ab1..50c81c81a 100644 --- a/xwords4/android/XWords4/project.properties +++ b/xwords4/android/XWords4/project.properties @@ -10,4 +10,4 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=android-8 +target=Google Inc.:Google APIs:11 diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 15324501a..1d1adc7e4 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -94,6 +94,7 @@ key_notagain_trading key_na_lookup key_na_browse + key_na_browseall key_na_values key_enable_debug key_download_path diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 9cf08491d..75140c3ae 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -1860,6 +1860,9 @@ This button opens the wordlist browser on the current player\'s wordlist. + This button opens the wordlist + browser on the wordlist of your choice. + The wordlist %s contains only tile information. There are no words to browse. @@ -2149,7 +2152,7 @@ Name group The group for new games - cannot be deleted." + cannot be deleted. Moving is impossible until there is more than one group. @@ -2164,4 +2167,7 @@ Even if they can be taller Move game %s + + This feature requires a newer version + of Android. diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 698f2144c..78648f1e4 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -102,6 +102,7 @@ public class BoardActivity extends XWActivity private static final int BT_PICK_ACTION = 17; private static final int SMS_PICK_ACTION = 18; private static final int SMS_CONFIG_ACTION = 19; + private static final int BUTTON_BROWSEALL_ACTION = 20; private static final String DLG_TITLE = "DLG_TITLE"; private static final String DLG_TITLESTR = "DLG_TITLESTR"; @@ -890,6 +891,15 @@ public class BoardActivity extends XWActivity String dictName = m_gi.dictName( m_view.getCurPlayer() ); DictBrowseActivity.launch( this, dictName ); break; + case BUTTON_BROWSEALL_ACTION: + View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT ); + if ( !DictsActivity.handleDictsPopup( this, button ) ) { + // HACK: a static method can't easily call + // showOKOnlyDialog, so assume we know the reason + // for failure. + showOKOnlyDialog( R.string.needs_newer ); + } + break; case PREV_HINT_ACTION: cmd = JNICmd.CMD_PREV_HINT; break; @@ -1807,6 +1817,10 @@ public class BoardActivity extends XWActivity R.string.not_again_browse, R.string.key_na_browse, BUTTON_BROWSE_ACTION ); + m_toolbar.setLongClickListener( Toolbar.BUTTON_BROWSE_DICT, + R.string.not_again_browseall, + R.string.key_na_browseall, + BUTTON_BROWSEALL_ACTION ); m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV, R.string.not_again_hintprev, R.string.key_notagain_hintprev, diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index d8f305ea1..b1cd04ce3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -36,6 +36,7 @@ import android.preference.PreferenceManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; @@ -44,9 +45,12 @@ import android.widget.Button; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ExpandableListView; +import android.widget.PopupMenu; import android.widget.TextView; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; + import junit.framework.Assert; import org.eehouse.android.xw4.DictUtils.DictAndLoc; @@ -59,6 +63,11 @@ public class DictsActivity extends XWExpandableListActivity MountEventReceiver.SDCardNotifiee, DlgDelegate.DlgClickNotify, DictImportActivity.DownloadFinishedListener { + private static interface SafePopup { + public void doPopup( Context context, View button ); + } + private static SafePopup s_safePopup = null; + private static final String DICT_DOLAUNCH = "do_launch"; private static final String DICT_LANG_EXTRA = "use_lang"; private static final String DICT_NAME_EXTRA = "use_dict"; @@ -76,6 +85,11 @@ public class DictsActivity extends XWExpandableListActivity private static final int MOVE_DICT = DlgDelegate.DIALOG_LAST + 1; private static final int SET_DEFAULT = DlgDelegate.DIALOG_LAST + 2; private static final int DICT_OR_DECLINE = DlgDelegate.DIALOG_LAST + 3; + + // I can't provide a subclass of MenuItem to hold DictAndLoc, so + // settle for a hash on the side. + private static HashMap s_itemData; + private int m_lang = 0; private String[] m_langs; private String m_name = null; @@ -779,5 +793,48 @@ public class DictsActivity extends XWExpandableListActivity } } + private static class SafePopupImpl implements SafePopup { + public void doPopup( final Context context, View button ) { + + MenuItem.OnMenuItemClickListener listener = + new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick( MenuItem item ) + { + DictAndLoc dal = s_itemData.get( item ); + s_itemData = null; + DictBrowseActivity.launch( context, dal.name, dal.loc ); + + return true; + } + }; + + s_itemData = new HashMap(); + PopupMenu popup = new PopupMenu( context, button ); + Menu menu = popup.getMenu(); + DictAndLoc[] dals = DictUtils.dictList( context ); + for ( DictAndLoc dal : dals ) { + MenuItem item = menu.add( dal.name ); + item.setOnMenuItemClickListener( listener ); + s_itemData.put( item, dal ); + } + popup.show(); + } + } + + public static boolean handleDictsPopup( Context context, View button ) + { + if ( null == s_safePopup ) { + int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK ); + if ( 11 <= sdkVersion ) { + s_safePopup = new SafePopupImpl(); + } + } + + boolean canHandle = null != s_safePopup; + if ( canHandle ) { + s_safePopup.doPopup( context, button ); + } + return canHandle; + } } \ No newline at end of file diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java index e36322a14..d1e281595 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java @@ -92,15 +92,29 @@ public class Toolbar { } } - public void setListener( int index, View.OnClickListener listener ) + public ImageButton getViewFor( int index ) { TBButtonInfo info = s_buttonInfo[index]; ImageButton button = (ImageButton)m_activity.findViewById( info.m_id ); + return button; + } + + private void setListener( int index, View.OnClickListener listener ) + { + ImageButton button = getViewFor( index ); if ( null != button ) { button.setOnClickListener( listener ); } } + private void setLongClickListener( int index, View.OnLongClickListener listener ) + { + ImageButton button = getViewFor( index ); + if ( null != button ) { + button.setOnLongClickListener( listener ); + } + } + public void setListener( int index, final int msgID, final int prefsKey, final int callback ) { @@ -112,6 +126,18 @@ public class Toolbar { setListener( index, listener ); } + public void setLongClickListener( int index, final int msgID, final int prefsKey, + final int callback ) + { + View.OnLongClickListener listener = new View.OnLongClickListener() { + public boolean onLongClick( View view ) { + m_activity.showNotAgainDlgThen( msgID, prefsKey, callback ); + return true; + } + }; + setLongClickListener( index, listener ); + } + public void update( int index, boolean enable ) { TBButtonInfo info = s_buttonInfo[index]; From c6d06e5fa4d4baab83b59a0129093460dc48d188 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 06:59:08 -0700 Subject: [PATCH 013/126] bring up new popup on tap rather than long-tap of browser button to make it easier to discover. Long-tap now opens the wordlist directly. --- .../android/XWords4/res/values/strings.xml | 3 +- .../eehouse/android/xw4/BoardActivity.java | 29 +++++++--------- .../eehouse/android/xw4/DictsActivity.java | 34 +++++++++++++++---- .../org/eehouse/android/xw4/GamesList.java | 4 +-- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 75140c3ae..be763ffcb 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -2168,6 +2168,5 @@ Move game %s - This feature requires a newer version - of Android. + Wordlist browser diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 78648f1e4..235d9deea 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -1,6 +1,6 @@ /* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */ /* - * Copyright 2009 - 2012 by Eric House (xwords@eehouse.org). All + * Copyright 2009 - 2013 by Eric House (xwords@eehouse.org). All * rights reserved. * * This program is free software; you can redistribute it and/or @@ -887,18 +887,15 @@ public class BoardActivity extends XWActivity Utils.showToast( BoardActivity.this, m_toastStr ); m_toastStr = null; break; - case BUTTON_BROWSE_ACTION: - String dictName = m_gi.dictName( m_view.getCurPlayer() ); - DictBrowseActivity.launch( this, dictName ); - break; case BUTTON_BROWSEALL_ACTION: + case BUTTON_BROWSE_ACTION: + String curDict = m_gi.dictName( m_view.getCurPlayer() ); View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT ); - if ( !DictsActivity.handleDictsPopup( this, button ) ) { - // HACK: a static method can't easily call - // showOKOnlyDialog, so assume we know the reason - // for failure. - showOKOnlyDialog( R.string.needs_newer ); + if ( BUTTON_BROWSEALL_ACTION == id && + DictsActivity.handleDictsPopup( this, button, curDict ) ) { + break; } + DictBrowseActivity.launch( this, curDict ); break; case PREV_HINT_ACTION: cmd = JNICmd.CMD_PREV_HINT; @@ -1814,13 +1811,13 @@ public class BoardActivity extends XWActivity private void populateToolbar() { m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT, - R.string.not_again_browse, - R.string.key_na_browse, - BUTTON_BROWSE_ACTION ); + R.string.not_again_browseall, + R.string.key_na_browseall, + BUTTON_BROWSEALL_ACTION ); m_toolbar.setLongClickListener( Toolbar.BUTTON_BROWSE_DICT, - R.string.not_again_browseall, - R.string.key_na_browseall, - BUTTON_BROWSEALL_ACTION ); + R.string.not_again_browse, + R.string.key_na_browse, + BUTTON_BROWSE_ACTION ); m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV, R.string.not_again_hintprev, R.string.key_notagain_hintprev, diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index b1cd04ce3..cb931e8d4 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -64,7 +64,7 @@ public class DictsActivity extends XWExpandableListActivity DictImportActivity.DownloadFinishedListener { private static interface SafePopup { - public void doPopup( Context context, View button ); + public void doPopup( Context context, View button, String curDict ); } private static SafePopup s_safePopup = null; @@ -794,7 +794,8 @@ public class DictsActivity extends XWExpandableListActivity } private static class SafePopupImpl implements SafePopup { - public void doPopup( final Context context, View button ) { + public void doPopup( final Context context, View button, + String curDict ) { MenuItem.OnMenuItemClickListener listener = new MenuItem.OnMenuItemClickListener() { @@ -802,8 +803,13 @@ public class DictsActivity extends XWExpandableListActivity { DictAndLoc dal = s_itemData.get( item ); s_itemData = null; - DictBrowseActivity.launch( context, dal.name, dal.loc ); - + + if ( null == dal ) { + DictsActivity.start( context ); + } else { + DictBrowseActivity.launch( context, dal.name, + dal.loc ); + } return true; } }; @@ -811,9 +817,16 @@ public class DictsActivity extends XWExpandableListActivity s_itemData = new HashMap(); PopupMenu popup = new PopupMenu( context, button ); Menu menu = popup.getMenu(); + menu.add( R.string.show_wordlist_browser ) + .setOnMenuItemClickListener( listener ); + + // Add at top but save until have dal info + MenuItem curItem = menu.add( curDict ); + DictAndLoc[] dals = DictUtils.dictList( context ); for ( DictAndLoc dal : dals ) { - MenuItem item = menu.add( dal.name ); + MenuItem item = dal.name.equals(curDict) + ? curItem : menu.add( dal.name ); item.setOnMenuItemClickListener( listener ); s_itemData.put( item, dal ); } @@ -821,7 +834,8 @@ public class DictsActivity extends XWExpandableListActivity } } - public static boolean handleDictsPopup( Context context, View button ) + public static boolean handleDictsPopup( Context context, View button, + String curDict ) { if ( null == s_safePopup ) { int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK ); @@ -832,9 +846,15 @@ public class DictsActivity extends XWExpandableListActivity boolean canHandle = null != s_safePopup; if ( canHandle ) { - s_safePopup.doPopup( context, button ); + s_safePopup.doPopup( context, button, curDict ); } return canHandle; } + public static void start( Context context ) + { + Intent intent = new Intent( context, DictsActivity.class ); + context.startActivity( intent ); + } + } \ No newline at end of file diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 464307ab6..e687fc5ec 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -669,7 +669,6 @@ public class GamesList extends XWExpandableListActivity public boolean onOptionsItemSelected( MenuItem item ) { boolean handled = true; - Intent intent; switch (item.getItemId()) { case R.id.gamel_menu_newgame: @@ -681,8 +680,7 @@ public class GamesList extends XWExpandableListActivity break; case R.id.gamel_menu_dicts: - intent = new Intent( this, DictsActivity.class ); - startActivity( intent ); + DictsActivity.start( this ); break; case R.id.gamel_menu_checkmoves: From ca8a37d8229806a541bac460c06ee1951f8df44c Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 07:33:31 -0700 Subject: [PATCH 014/126] move less-used buttons to right where they may be off-screen --- xwords4/android/XWords4/res/layout/board.xml | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/xwords4/android/XWords4/res/layout/board.xml b/xwords4/android/XWords4/res/layout/board.xml index 06adf425d..a6acf5fb5 100644 --- a/xwords4/android/XWords4/res/layout/board.xml +++ b/xwords4/android/XWords4/res/layout/board.xml @@ -57,22 +57,10 @@ style="@style/toolbar_button" android:src="@drawable/shuffle" /> - - - + + + From f9676f5ffd70cca5fa29d8f3b2598187da41b6b1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 07:34:31 -0700 Subject: [PATCH 015/126] set focusableInTouchMode on wordlist browser's layout. This is the only way I can find to prevent the soft keyboard from coming up on launch. --- xwords4/android/XWords4/res/layout/dict_browser.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/res/layout/dict_browser.xml b/xwords4/android/XWords4/res/layout/dict_browser.xml index 342fe3355..36259c7c2 100644 --- a/xwords4/android/XWords4/res/layout/dict_browser.xml +++ b/xwords4/android/XWords4/res/layout/dict_browser.xml @@ -5,7 +5,9 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:paddingLeft="8dp" - android:paddingRight="8dp"> + android:paddingRight="8dp" + android:focusableInTouchMode="true" + > Date: Wed, 5 Jun 2013 08:03:13 -0700 Subject: [PATCH 016/126] fix bad message when dict deleted by using the right rowid variable -- duh. --- .../android/XWords4/src/org/eehouse/android/xw4/GamesList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index e687fc5ec..a082fe1ac 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -135,7 +135,7 @@ public class GamesList extends XWExpandableListActivity String message; String langName = DictLangCache.getLangName( this, m_missingDictLang ); - String gameName = GameUtils.getName( this, m_rowid ); + String gameName = GameUtils.getName( this, m_missingDictRowId ); if ( WARN_NODICT == id ) { message = getString( R.string.no_dictf, gameName, langName ); From 6b2ec193c24195dbc85235dee76327f4aa1745d9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 08:04:00 -0700 Subject: [PATCH 017/126] cleanup --- .../XWords4/src/org/eehouse/android/xw4/GameListItem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java index 9ffb048df..4a40b155c 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListItem.java @@ -189,12 +189,12 @@ public class GameListItem extends LinearLayout break; } + String name = GameUtils.getName( m_context, m_rowid ); if ( null != value ) { - String name = GameUtils.getName( m_context, m_rowid ); value = m_context.getString( R.string.str_game_namef, name, value ); } else { - value = GameUtils.getName( m_context, m_rowid ); + value = name; } view.setText( value ); From 6a9c0bf5d48bfebbba53fc36ba78ba51cc5c3030 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 20:56:59 -0700 Subject: [PATCH 018/126] up version strings for next beta --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- xwords4/android/XWords4/res/raw/changes | 19 +++++++++---------- .../android/XWords4/res/values/app_name.xml | 2 +- .../src/org/eehouse/android/xw4/XWApp.java | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 682161ac9..396ca35ab 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index 80f99854d..c4c1b912f 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -5,26 +5,25 @@ -Crosswords 4.4 beta 60 release +Crosswords 4.4 beta 61 release

New with this release

    -
  • Allow alternate spellings for tiles in the Find field in the - wordlist browser, e.g. 'a' for 'A' and 'L-L' for 'L·L' (in - Catalan). The new wordlist format requires this upgrade, so I will - wait a few weeks before releasing new wordlists.
  • +
  • Reduce amount of network traffic required to get moves
  • -
  • Upgrade built-in English wordlists.
  • +
  • Make all wordlists available from wordlists button below board + (on Android 3.0 and above)
  • -
  • Don't run SMSService if play via SSM is disabled
  • +
  • Dim pending score counter (at right end of tray) when it's not + that player's turn or game's over
  • + +
  • Fix a few bugs
  • -
  • Fix bug with invites to SMS games where invitee is missing - wordlist

Next up

    -
  • Improve communication with relay
  • +
  • Improve communication with relay, part 2

(The full changelog diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 324c6a8e8..332550f5b 100644 --- a/xwords4/android/XWords4/res/values/app_name.xml +++ b/xwords4/android/XWords4/res/values/app_name.xml @@ -1,5 +1,5 @@ - 4.4 beta 60 + 4.4 beta 61 diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java index d3d02f534..1f0790181 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWApp.java @@ -33,7 +33,7 @@ public class XWApp extends Application { public static final boolean GCMSUPPORTED = true; public static final boolean ATTACH_SUPPORTED = true; public static final boolean REMATCH_SUPPORTED = false; - public static final boolean DEBUG = true; + public static final boolean DEBUG = false; public static final boolean DEBUG_LOCKS = false && DEBUG; public static final boolean DEBUG_EXP_TIMERS = false && DEBUG; From 00be532c962e8068f9e600251df8cef40750bcb6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 21:11:37 -0700 Subject: [PATCH 019/126] fix formatting -- no code change --- .../android/xw4/DictImportActivity.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java index 6b3e0e20d..a3237d22f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictImportActivity.java @@ -136,21 +136,21 @@ public class DictImportActivity extends XWActivity { } } // class DownloadFilesTask - @Override - protected void onCreate( Bundle savedInstanceState ) + @Override + protected void onCreate( Bundle savedInstanceState ) { - super.onCreate( savedInstanceState ); + super.onCreate( savedInstanceState ); DownloadFilesTask dft = null; - requestWindowFeature( Window.FEATURE_LEFT_ICON ); - setContentView( R.layout.import_dict ); - getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON, + requestWindowFeature( Window.FEATURE_LEFT_ICON ); + setContentView( R.layout.import_dict ); + getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON, R.drawable.icon48x48 ); - ProgressBar progressBar = (ProgressBar)findViewById( R.id.progress_bar ); + ProgressBar progressBar = (ProgressBar)findViewById( R.id.progress_bar ); - Intent intent = getIntent(); - Uri uri = intent.getData(); + Intent intent = getIntent(); + Uri uri = intent.getData(); if ( null == uri ) { String url = intent.getStringExtra( APK_EXTRA ); boolean isApp = null != url; @@ -175,10 +175,10 @@ public class DictImportActivity extends XWActivity { String msg = getString( R.string.downloading_dictf, showName ); TextView view = (TextView)findViewById( R.id.dwnld_message ); view.setText( msg ); - + dft.execute( uri ); } - } + } private File saveToDownloads( InputStream is, String name ) { From ed1e2a0812a0d65c6441d01b0f92b4f8a798f6ab Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 5 Jun 2013 21:14:51 -0700 Subject: [PATCH 020/126] fix formatting -- no code change --- .../eehouse/android/xw4/BoardActivity.java | 2 +- .../eehouse/android/xw4/FirstRunDialog.java | 54 +++++++++---------- .../eehouse/android/xw4/GCMIntentService.java | 2 +- .../org/eehouse/android/xw4/GameUtils.java | 2 +- .../src/org/eehouse/android/xw4/Utils.java | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index 235d9deea..eadcc7733 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -150,7 +150,7 @@ public class BoardActivity extends XWActivity private boolean m_gameOver = false; // call startActivityForResult synchronously - private Semaphore m_forResultWait = new Semaphore(0); + private Semaphore m_forResultWait = new Semaphore(0); private int m_resultCode; private Thread m_blockingThread; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/FirstRunDialog.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/FirstRunDialog.java index ef23499d7..2e2365bd0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/FirstRunDialog.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/FirstRunDialog.java @@ -37,48 +37,48 @@ public class FirstRunDialog { { String page = null; InputStream inputStream = null; - try { + try { inputStream = context.getResources() .openRawResource(R.raw.changes); - - final char[] buf = new char[0x1000]; - StringBuilder stringBuilder = new StringBuilder(); - Reader reader = new InputStreamReader( inputStream, "UTF-8" ); - int nRead; - do { + + final char[] buf = new char[0x1000]; + StringBuilder stringBuilder = new StringBuilder(); + Reader reader = new InputStreamReader( inputStream, "UTF-8" ); + int nRead; + do { nRead = reader.read( buf, 0, buf.length ); if ( nRead > 0 ) { stringBuilder.append( buf, 0, nRead ); } - } while ( nRead >= 0 ); - - page = stringBuilder.toString(); - } - catch ( IOException ioe ) { - DbgUtils.loge( ioe ); - } - finally { + } while ( nRead >= 0 ); + + page = stringBuilder.toString(); + } + catch ( IOException ioe ) { + DbgUtils.loge( ioe ); + } + finally { // could just catch NPE.... - if ( null != inputStream ) { - try { - inputStream.close(); - } catch ( IOException ioe ) { + if ( null != inputStream ) { + try { + inputStream.close(); + } catch ( IOException ioe ) { DbgUtils.loge( ioe ); - } - } - } - + } + } + } + // This won't support e.g mailto refs. Probably want to // launch the browser with an intent eventually. - WebView view = new WebView( context ); - view.loadData( page, "text/html", "utf-8" ); + WebView view = new WebView( context ); + view.loadData( page, "text/html", "utf-8" ); - AlertDialog dialog = new AlertDialog.Builder( context ) + AlertDialog dialog = new AlertDialog.Builder( context ) .setIcon(android.R.drawable.ic_menu_info_details) .setTitle( R.string.changes_title ) .setView( view ) .setPositiveButton( R.string.button_ok, null) .create(); - dialog.show(); + dialog.show(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GCMIntentService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GCMIntentService.java index 99945ca0a..c6038450a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GCMIntentService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GCMIntentService.java @@ -34,7 +34,7 @@ public class GCMIntentService extends GCMBaseIntentService { super( GCMConsts.SENDER_ID ); } - @Override + @Override protected void onError( Context context, String error ) { DbgUtils.logf("GCMIntentService.onError(%s)", error ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 3a55b792f..4a4e4d9bd 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -282,7 +282,7 @@ public class GameUtils { } public static long saveNewGame( Context context, int gamePtr, - CurGameInfo gi ) + CurGameInfo gi ) { byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); GameLock lock = DBUtils.saveNewGame( context, stream ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java index 0c1895f90..035d5bb03 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java @@ -60,7 +60,7 @@ public class Utils { public static final int TURN_COLOR = 0x7F00FF00; private static final String DB_PATH = "XW_GAMES"; - private static final String HIDDEN_PREFS = "xwprefs_hidden"; + private static final String HIDDEN_PREFS = "xwprefs_hidden"; private static final String SHOWN_VERSION_KEY = "SHOWN_VERSION_KEY"; private static Boolean s_isFirstBootThisVersion = null; From bf767577e38147f05a20663d6cc10e33b57b19f5 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 7 Jun 2013 20:54:16 -0700 Subject: [PATCH 021/126] msg64 will not always be set! Don't delete in that case. --- xwords4/relay/scripts/gcm_loop.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/scripts/gcm_loop.py b/xwords4/relay/scripts/gcm_loop.py index e93378f8f..a1b51956b 100755 --- a/xwords4/relay/scripts/gcm_loop.py +++ b/xwords4/relay/scripts/gcm_loop.py @@ -112,7 +112,7 @@ def asGCMIds(con, devids, typ): def notifyGCM( devids, typ, target ): success = False if typ == DEVTYPE_GCM: - if 3 <= target['clntVers']: + if 3 <= target['clntVers'] and target['msg64']: connname = "%s/%d" % (target['connname'], target['hid']) data = { 'msgs64': [ target['msg64'] ], 'connname': connname, @@ -235,8 +235,9 @@ def main(): toDelete = [] for devid in targets.keys(): target = targets[devid] - if notifyGCM( asGCMIds(g_con, [devid], typ), typ, target )\ - and 3 <= target['clntVers']: + if notifyGCM( asGCMIds(g_con, [devid], typ), typ, target ) \ + and 3 <= target['clntVers'] \ + and target['msg64']: toDelete.append( str(target['id']) ) pruneSent( devids ) deleteMsgs( g_con, toDelete ) From e6488ff8c1a5d7ae02cda32b3110f1a7544513f1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 7 Jun 2013 20:54:57 -0700 Subject: [PATCH 022/126] save space by shortening column names --- xwords4/relay/scripts/showinplay.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/scripts/showinplay.sh b/xwords4/relay/scripts/showinplay.sh index dd9e940dd..f302ba16d 100755 --- a/xwords4/relay/scripts/showinplay.sh +++ b/xwords4/relay/scripts/showinplay.sh @@ -27,7 +27,7 @@ echo -n "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')" echo "; relay pid[s]: $(pidof xwrelay)" echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;") -echo "SELECT dead,connname,cid,room,lang,clntVers as cv ,ntotal,nperdevice,seeds,addrs,tokens,devids,ack,nsent as snt "\ +echo "SELECT dead,connname,cid,room,lang as lg,clntVers as cv ,ntotal as tot,nperdevice as nPerDev,seeds,addrs,tokens,devids,ack,nsent as snt "\ "FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \ | psql xwgames From c8f584dad2e67fcd1adbf673115e653b7dba889c Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Jun 2013 06:15:36 -0700 Subject: [PATCH 023/126] don't listen on stdin if it's been closed --- xwords4/linux/cursesmain.c | 4 +++- xwords4/linux/linuxmain.c | 5 ++--- xwords4/linux/main.h | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index a2e98c97b..370b053ea 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1743,7 +1743,9 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) #endif #ifdef USE_GLIBLOOP - cursesListenOnSocket( &g_globals, 0, handle_stdin ); + if ( !params->closeStdin ) { + cursesListenOnSocket( &g_globals, 0, handle_stdin ); + } setOneSecondTimer( &g_globals.cGlobals ); # ifdef DEBUG int piperesult = diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index dd3e0aba5..a001d7daf 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -1546,7 +1546,6 @@ main( int argc, char** argv ) XP_Bool isServer = XP_FALSE; char* portNum = NULL; char* hostName = "localhost"; - XP_Bool closeStdin = XP_FALSE; unsigned int seed = defaultRandomSeed(); LaunchParams mainParams; XP_U16 nPlayerDicts = 0; @@ -1871,7 +1870,7 @@ main( int argc, char** argv ) break; #endif case CMD_CLOSESTDIN: - closeStdin = XP_TRUE; + mainParams.closeStdin = XP_TRUE; break; case CMD_QUITAFTER: mainParams.quitAfter = atoi(optarg); @@ -2121,7 +2120,7 @@ main( int argc, char** argv ) srandom( seed ); /* init linux random number generator */ XP_LOGF( "seeded srandom with %d", seed ); - if ( closeStdin ) { + if ( mainParams.closeStdin ) { fclose( stdin ); if ( mainParams.quitAfter < 0 ) { fprintf( stderr, "stdin closed; you'll need some way to quit\n" ); diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index 028ea0b0a..2ae3bf294 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -89,6 +89,7 @@ typedef struct LaunchParams { XP_Bool duplicatePackets; XP_Bool skipGameOver; XP_Bool useMmap; + XP_Bool closeStdin; #ifdef XWFEATURE_SEARCHLIMIT XP_Bool allowHintRect; #endif From e89fd3e8cc09b035209313d5c5e9d538777100d7 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 10 Jun 2013 06:38:42 -0700 Subject: [PATCH 024/126] fix names to make units clear -- no code change --- xwords4/relay/crefmgr.cpp | 7 ++++--- xwords4/relay/timermgr.cpp | 10 +++++----- xwords4/relay/timermgr.h | 2 +- xwords4/relay/tpool.cpp | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/xwords4/relay/crefmgr.cpp b/xwords4/relay/crefmgr.cpp index 42075d39d..8c50b0e67 100644 --- a/xwords4/relay/crefmgr.cpp +++ b/xwords4/relay/crefmgr.cpp @@ -474,9 +474,10 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID cid, if ( m_cookieMap.size() == 1 ) { RelayConfigs* cfg = RelayConfigs::GetConfigs(); int heartbeat; - cfg->GetValueFor( "HEARTBEAT", &heartbeat ); - TimerMgr::GetTimerMgr()->SetTimer( heartbeat, heartbeatProc, this, - heartbeat ); + if ( cfg->GetValueFor( "HEARTBEAT", &heartbeat ) ) { + TimerMgr::GetTimerMgr()->SetTimer( heartbeat, heartbeatProc, this, + heartbeat ); + } } #endif diff --git a/xwords4/relay/timermgr.cpp b/xwords4/relay/timermgr.cpp index 530a7c8e6..41d434300 100644 --- a/xwords4/relay/timermgr.cpp +++ b/xwords4/relay/timermgr.cpp @@ -44,14 +44,14 @@ TimerMgr::GetTimerMgr() } void -TimerMgr::SetTimer( time_t inMillis, TimerProc proc, void* closure, +TimerMgr::SetTimer( time_t inSeconds, TimerProc proc, void* closure, int interval ) { logf( XW_LOGINFO, "%s: uptime = %ld", __func__, uptime() ); TimerInfo ti; ti.proc = proc; ti.closure = closure; - ti.when = uptime() + inMillis; + ti.when = uptime() + inSeconds; ti.interval = interval; MutexLock ml( &m_timersMutex ); @@ -68,7 +68,7 @@ TimerMgr::SetTimer( time_t inMillis, TimerProc proc, void* closure, } time_t -TimerMgr::GetPollTimeout() +TimerMgr::GetPollTimeoutMillis() { MutexLock ml( &m_timersMutex ); @@ -80,10 +80,10 @@ TimerMgr::GetPollTimeout() if ( tout < 0 ) { tout = 0; } - tout *= 1000; + tout *= 1000; /* convert to milliseconds */ } return tout; -} /* GetPollTimeout */ +} /* GetPollTimeoutMillis */ bool TimerMgr::getTimer( TimerProc proc, void* closure ) diff --git a/xwords4/relay/timermgr.h b/xwords4/relay/timermgr.h index c994780f2..841833b71 100644 --- a/xwords4/relay/timermgr.h +++ b/xwords4/relay/timermgr.h @@ -41,7 +41,7 @@ class TimerMgr { int interval ); /* 0 means non-recurring */ void ClearTimer( TimerProc proc, void* closure ); - time_t GetPollTimeout(); + time_t GetPollTimeoutMillis(); void FireElapsedTimers(); private: diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index 2aff5caab..ffb850281 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -349,7 +349,7 @@ XWThreadPool::real_listener() } pthread_rwlock_unlock( &m_activeSocketsRWLock ); - int nMillis = tmgr->GetPollTimeout(); + int nMillis = tmgr->GetPollTimeoutMillis(); #ifdef LOG_POLL logf( XW_LOGINFO, "polling %s nmillis=%d", log, nMillis ); From 5da4936e43825fa573013f93e797a263a8f1119b Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 06:42:21 -0700 Subject: [PATCH 025/126] report unexpected received packet size as an error --- xwords4/linux/linuxmain.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index a001d7daf..39f103e72 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -999,7 +999,9 @@ linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize ) unsigned short packetSize = ntohs( tmp ); assert( packetSize <= bufSize ); nRead = blocking_read( sock, buf, packetSize ); - if ( nRead == packetSize ) { + if ( nRead != packetSize ) { + nRead = -1; + } else { LaunchParams* params = cGlobals->params; ++params->nPacketsRcvd; if ( params->dropNthRcvd == 0 ) { From 7ce939f5c07d864fd795bea03e27df66faa2c7c9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 06:48:22 -0700 Subject: [PATCH 026/126] pass rather than hard-coding column name --- xwords4/relay/dbmgr.cpp | 10 +++++----- xwords4/relay/dbmgr.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 71014f8e3..7812285ca 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -384,7 +384,7 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion, if ( newID == HOST_ID_NONE ) { int arr[4] = {0}; - readArray( connName, arr ); + readArray( connName, "nPerDevice", arr ); for ( newID = HOST_ID_SERVER; newID <= 4; ++newID ) { if ( arr[newID-1] == 0 ) { break; @@ -709,12 +709,12 @@ DBMgr::execSql( const char* const query ) } void -DBMgr::readArray( const char* const connName, int arr[] ) /* len 4 */ +DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /* len 4 */ { - const char* fmt = "SELECT nPerDevice FROM " GAMES_TABLE " WHERE connName='%s'"; + const char* fmt = "SELECT %s FROM " GAMES_TABLE " WHERE connName='%s'"; string query; - string_printf( query, fmt, connName ); + string_printf( query, fmt, column, connName ); logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); PGresult* result = PQexec( getThreadConn(), query.c_str() ); @@ -768,7 +768,7 @@ DBMgr::getDevID( const DevID* devID ) } PQclear( result ); } - logf( XW_LOGINFO, "%s(in=%s)=>%d (0x.8X)", __func__, + logf( XW_LOGINFO, "%s(in=%s)=>%d (0x%.8X)", __func__, devID->m_devIDString.c_str(), rDevID, rDevID ); return rDevID; } diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 6f3c0d2ef..8e12fc65f 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -121,7 +121,7 @@ class DBMgr { DBMgr(); bool execSql( const string& query ); bool execSql( const char* const query ); /* no-results query */ - void readArray( const char* const connName, int arr[] ); + void readArray( const char* const connName, const char* column, int arr[] ); DevIDRelay getDevID( const char* connName, int hid ); DevIDRelay getDevID( const DevID* devID ); int getCountWhere( const char* table, string& test ); From 192855444448628ca6577c72f2dfb73cf25b71ee Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 06:54:38 -0700 Subject: [PATCH 027/126] add asserts and clarifying comments --- xwords4/relay/crefmgr.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/crefmgr.cpp b/xwords4/relay/crefmgr.cpp index 8c50b0e67..36cad1f94 100644 --- a/xwords4/relay/crefmgr.cpp +++ b/xwords4/relay/crefmgr.cpp @@ -304,6 +304,7 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie, &curLangCode, &nPlayersT, &nAlreadyHere, isDead ); if ( 0 != cid ) { /* already open */ + assert( nPlayersT == nPlayersS ); cinfo = m_cidlock->Claim( cid ); if ( NULL == cinfo->GetRef() ) { m_cidlock->Relinquish( cinfo, true ); @@ -327,6 +328,7 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie, cookie = curCookie; } + assert( nPlayersT == nPlayersS ); cref = AddNew( cookie, connName, cid, curLangCode, nPlayersT, nAlreadyHere ); cinfo->SetRef( cref ); @@ -402,12 +404,12 @@ CRefMgr::PrintSocketInfo( int socket, string& out ) } CidInfo* -CRefMgr::getCookieRef( CookieID cid, bool failOk ) +CRefMgr::getCookieRef( CookieID cid, bool failOk /* = false */ ) { CidInfo* cinfo = NULL; for ( int count = 0; ; ++count ) { cinfo = m_cidlock->Claim( cid ); - if ( NULL != cinfo->GetRef() ) { + if ( NULL != cinfo->GetRef() ) { /* What's it mean to get a cinfo back but have it be empty??? */ break; } else if ( failOk || count > 20 ) { break; @@ -666,7 +668,7 @@ SafeCref::SafeCref( const char* const connName ) } } -SafeCref::SafeCref( CookieID cid, bool failOk ) +SafeCref::SafeCref( CookieID cid, bool failOk /* = false */ ) : m_cinfo( NULL ) , m_mgr( CRefMgr::Get() ) , m_isValid( false ) From a546c025d5a4a9fff5b684f92ac3cf455aeaf5ca Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 07:25:25 -0700 Subject: [PATCH 028/126] Rather than queuing sockets needing reading, read them immediately and queue the packets for processing. Add ids so they can be tracked in the logs. In addition to making tcp and udp packet processing more similar this fixes the case where a read is delayed until after the client has closed the connection (and so returns an error.) --- xwords4/relay/tpool.cpp | 94 +++++++++++++++++++++++++------------- xwords4/relay/tpool.h | 2 + xwords4/relay/udpqueue.cpp | 38 +++++++++++++++ xwords4/relay/udpqueue.h | 6 ++- 4 files changed, 108 insertions(+), 32 deletions(-) diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index ffb850281..396c5a4b1 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -130,6 +130,31 @@ XWThreadPool::AddSocket( SockType stype, QueueCallback proc, const AddrInfo* fro interrupt_poll(); } +bool +XWThreadPool::SocketFound( const AddrInfo* addr ) +{ + assert( addr->isTCP() ); + bool found = false; + { + RWWriteLock ml( &m_activeSocketsRWLock ); + + logf( XW_LOGINFO, "%s: START: %d sockets active", __func__, + m_activeSockets.size() ); + + vector::iterator iter; + for ( iter = m_activeSockets.begin(); + iter != m_activeSockets.end(); ++iter ) { + if ( iter->m_addr.equals( *addr ) ) { + found = true; + break; + } + } + logf( XW_LOGINFO, "%s: AFTER: %d sockets active", __func__, + m_activeSockets.size() ); + } + return found; +} + bool XWThreadPool::RemoveSocket( const AddrInfo* addr ) { @@ -196,29 +221,29 @@ XWThreadPool::EnqueueKill( const AddrInfo* addr, const char* const why ) } } -bool -XWThreadPool::get_process_packet( SockType stype, QueueCallback proc, const AddrInfo* addr ) -{ - bool success = false; - short packetSize; - assert( sizeof(packetSize) == 2 ); +// bool +// XWThreadPool::get_process_packet( SockType stype, QueueCallback proc, const AddrInfo* addr ) +// { +// bool success = false; +// short packetSize; +// assert( sizeof(packetSize) == 2 ); - // Fix this to return an allocated buffer - unsigned char buf[MAX_MSG_LEN+1]; - int nRead = read_packet( addr->socket(), buf, sizeof(buf) ); - if ( nRead < 0 ) { - EnqueueKill( addr, "bad packet" ); - } else if ( STYPE_PROXY == stype && NULL != proc ) { - buf[nRead] = '\0'; - UdpQueue::get()->handle( addr, buf, nRead+1, proc ); - } else if ( STYPE_GAME == stype && NULL != proc ) { - UdpQueue::get()->handle( addr, buf, nRead, proc ); - success = true; - } else { - assert(0); - } - return success; -} /* get_process_packet */ +// // Fix this to return an allocated buffer +// unsigned char buf[MAX_MSG_LEN+1]; +// int nRead = read_packet( addr->socket(), buf, sizeof(buf) ); +// if ( nRead < 0 ) { +// EnqueueKill( addr, "bad packet" ); +// } else if ( STYPE_PROXY == stype && NULL != proc ) { +// buf[nRead] = '\0'; +// UdpQueue::get()->handle( addr, buf, nRead+1, proc ); +// } else if ( STYPE_GAME == stype && NULL != proc ) { +// UdpQueue::get()->handle( addr, buf, nRead, proc ); +// success = true; +// } else { +// assert(0); +// } +// return success; +// } /* get_process_packet */ void* XWThreadPool::tpool_main( void* closure ) @@ -261,10 +286,11 @@ XWThreadPool::real_tpool_main( ThreadInfo* tip ) logf( XW_LOGINFO, "worker thread got socket %d from queue", socket ); switch ( pr.m_act ) { case Q_READ: - assert( socket >= 0 ); - if ( get_process_packet( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ) ) { - AddSocket( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ); - } + assert( 0 ); + // assert( socket >= 0 ); + // if ( get_process_packet( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ) ) { + // AddSocket( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ); + // } break; case Q_KILL: (*m_kFunc)( &pr.m_info.m_addr ); @@ -387,10 +413,12 @@ XWThreadPool::real_listener() for ( ii = 0; ii < nSockets && nEvents > 0; ++ii ) { if ( fds[curfd].revents != 0 ) { - int socket = fds[curfd].fd; - const AddrInfo* addr = &sinfos[curfd].m_addr; - assert( socket == addr->socket() ); - if ( !RemoveSocket( addr ) ) { + // int socket = fds[curfd].fd; + SockInfo* sinfo = &sinfos[curfd]; + const AddrInfo* addr = &sinfo->m_addr; + + assert( fds[curfd].fd == addr->socket() ); + if ( !SocketFound( addr ) ) { /* no further processing if it's been removed while we've been sleeping in poll */ --nEvents; @@ -398,10 +426,14 @@ XWThreadPool::real_listener() } if ( 0 != (fds[curfd].revents & (POLLIN | POLLPRI)) ) { - enqueue( sinfos[curfd] ); + if ( !UdpQueue::get()->handle( addr, sinfo->m_proc ) ) { + RemoveSocket( addr ); + EnqueueKill( addr, "bad packet" ); + } } else { logf( XW_LOGERROR, "odd revents: %x", fds[curfd].revents ); + RemoveSocket( addr ); EnqueueKill( addr, "error/hup in poll()" ); } --nEvents; diff --git a/xwords4/relay/tpool.h b/xwords4/relay/tpool.h index f8fa997ed..9ae0b1359 100644 --- a/xwords4/relay/tpool.h +++ b/xwords4/relay/tpool.h @@ -74,6 +74,8 @@ class XWThreadPool { /* Remove from set being listened on */ bool RemoveSocket( const AddrInfo* addr ); + /* test if is in set being listened on */ + bool SocketFound( const AddrInfo* addr ); void enqueue( QAction act = Q_READ ); void enqueue( SockInfo si, QAction act = Q_READ ); diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index 724d73c16..17eae630c 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include "udpqueue.h" #include "mlock.h" @@ -38,6 +39,7 @@ UdpThreadClosure::logStats() UdpQueue::UdpQueue() { + m_nextID = 0; pthread_mutex_init ( &m_queueMutex, NULL ); pthread_cond_init( &m_queueCondVar, NULL ); @@ -63,12 +65,47 @@ UdpQueue::get() return s_instance; } +bool +UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) +{ + bool success = false; + int sock = addr->socket(); + unsigned short msgLen; + ssize_t nRead = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL ); + if ( 0 == nRead ) { + logf( XW_LOGINFO, "%s: recv(sock=%d) => 0: remote closed", __func__, sock ); + } else if ( nRead != sizeof(msgLen) ) { + logf( XW_LOGERROR, "%s: first recv => %d: %s", __func__, + nRead, strerror(errno) ); + } else { + msgLen = ntohs( msgLen ); + if ( MAX_MSG_LEN <= msgLen ) { + logf( XW_LOGERROR, "%s: message of len %d too large; dropping", __func__, msgLen ); + } else { + unsigned char buf[msgLen]; + nRead = recv( sock, buf, msgLen, MSG_WAITALL ); + if ( nRead == msgLen ) { + logf( XW_LOGINFO, "%s: read %d bytes on socket %d", __func__, nRead, sock ); + handle( addr, buf, msgLen, cb ); + success = true; + } else { + logf( XW_LOGERROR, "%s: second recv failed: %s", __func__, + strerror(errno) ); + } + } + } + return success; +} + void UdpQueue::handle( const AddrInfo* addr, unsigned char* buf, int len, QueueCallback cb ) { UdpThreadClosure* utc = new UdpThreadClosure( addr, buf, len, cb ); MutexLock ml( &m_queueMutex ); + int id = ++m_nextID; + utc->setID( id ); + logf( XW_LOGINFO, "%s: enqueuing packet %d", __func__, id ); m_queue.push_back( utc ); pthread_cond_signal( &m_queueCondVar ); } @@ -86,6 +123,7 @@ UdpQueue::thread_main() pthread_mutex_unlock( &m_queueMutex ); utc->noteDequeued(); + logf( XW_LOGINFO, "%s: dispatching packet %d", __func__, utc->getID() ); (*utc->cb())( utc ); utc->logStats(); delete utc; diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index 57cbda212..a2ee730a8 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -55,6 +55,8 @@ public: void noteDequeued() { m_dequed = time( NULL ); } void logStats(); const QueueCallback cb() const { return m_cb; } + void setID( int id ) { m_id = id; } + int getID( void ) { return m_id; } private: unsigned char* m_buf; @@ -63,6 +65,7 @@ public: QueueCallback m_cb; time_t m_created; time_t m_dequed; + int m_id; }; class UdpQueue { @@ -70,6 +73,7 @@ class UdpQueue { static UdpQueue* get(); UdpQueue(); ~UdpQueue(); + bool handle( const AddrInfo* addr, QueueCallback cb ); void handle( const AddrInfo* addr, unsigned char* buf, int len, QueueCallback cb ); @@ -80,7 +84,7 @@ class UdpQueue { pthread_mutex_t m_queueMutex; pthread_cond_t m_queueCondVar; deque m_queue; - + int m_nextID; }; #endif From 950e754ca2e55417a698c6b90eb5223f9179ad11 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 07:26:10 -0700 Subject: [PATCH 029/126] cleanup --- xwords4/relay/crefmgr.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/xwords4/relay/crefmgr.h b/xwords4/relay/crefmgr.h index 7f657c2ab..242f7fbf3 100644 --- a/xwords4/relay/crefmgr.h +++ b/xwords4/relay/crefmgr.h @@ -187,14 +187,15 @@ class SafeCref { bool Forward( HostID src, const AddrInfo* addr, HostID dest, const unsigned char* buf, int buflen ) { - if ( IsValid() ) { + bool success = IsValid(); + if ( success ) { CookieRef* cref = m_cinfo->GetRef(); assert( 0 != cref->GetCid() ); cref->_Forward( src, addr, dest, buf, buflen ); - return true; } else { - return false; + logf( XW_LOGINFO, "%s: unable to forward", __func__ ); } + return success; } void PutMsg( HostID srcID, const AddrInfo* addr, HostID destID, @@ -252,14 +253,14 @@ class SafeCref { } bool HandleAck(HostID hostID ) { - if ( IsValid() ) { + bool handled = IsValid(); + if ( handled ) { CookieRef* cref = m_cinfo->GetRef(); assert( 0 != cref->GetCid() ); cref->_HandleAck( hostID ); - return true; - } else { - return false; } + logf( XW_LOGINFO, "%s => %d", __func__, handled ); + return handled; } void Shutdown() { if ( IsValid() ) { From 3e9dfb9a9db109642a1ff977f7845fa88b10152b Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 07:26:54 -0700 Subject: [PATCH 030/126] allow a thread to get a cidlock it already has. --- xwords4/relay/cidlock.cpp | 23 +++++++++++++++++++++-- xwords4/relay/cidlock.h | 8 ++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/xwords4/relay/cidlock.cpp b/xwords4/relay/cidlock.cpp index 76c7df7bd..b9ab983a4 100644 --- a/xwords4/relay/cidlock.cpp +++ b/xwords4/relay/cidlock.cpp @@ -34,6 +34,22 @@ CidInfo::GetAddrs( void ) m_addrs : m_cref->GetAddrs(); } +void +CidInfo::SetOwner( pthread_t owner ) +{ + if ( 0 == owner ) { + if ( 0 == --m_ownerCount ) { + m_owner = 0; + } + } else { + ++m_ownerCount; + assert( 0 == m_owner || owner == m_owner ); + m_owner = owner; + } + assert( 0 <= m_ownerCount ); + logf( XW_LOGINFO, "%s(owner=%d); m_ownerCount=%d", __func__, owner, m_ownerCount ); +} + CidLock* CidLock::s_instance = NULL; CidLock::CidLock() : m_nextCID(0) @@ -79,6 +95,7 @@ CidLock::Claim( CookieID cid ) logf( XW_LOGINFO, "%s(%d)", __func__, cid ); #endif CidInfo* info = NULL; + pthread_t self = pthread_self(); for ( ; ; ) { MutexLock ml( &m_infos_mutex ); @@ -92,13 +109,14 @@ CidLock::Claim( CookieID cid ) info = new CidInfo( cid ); m_infos.insert( pair( cid, info ) ); } else { - if ( 0 == iter->second->GetOwner() ) { + pthread_t owner = iter->second->GetOwner(); + if ( 0 == owner || self == owner ) { info = iter->second; } } if ( NULL != info ) { // we're done - info->SetOwner( pthread_self() ); + info->SetOwner( self ); PRINT_CLAIMED(); break; } @@ -174,6 +192,7 @@ CidLock::Relinquish( CidInfo* claim, bool drop ) logf( XW_LOGINFO, "%s: deleting %p", __func__, iter->second ); #endif m_infos.erase( iter ); + claim->SetOwner( 0 ); delete claim; } else { CookieRef* ref = claim->GetRef(); diff --git a/xwords4/relay/cidlock.h b/xwords4/relay/cidlock.h index cb0678e08..2a2e75c1e 100644 --- a/xwords4/relay/cidlock.h +++ b/xwords4/relay/cidlock.h @@ -34,7 +34,10 @@ class CidInfo { CidInfo( CookieID cid ) :m_cid(cid), m_cref(NULL), - m_owner(0) {} + m_owner(0), + m_ownerCount(0) {} + + ~CidInfo() { assert( 0 == m_ownerCount ); } CookieID GetCid( void ) { return m_cid; } CookieRef* GetRef( void ) { return m_cref; } @@ -43,12 +46,13 @@ class CidInfo { void SetAddrs( vector addrs ) { m_addrs = addrs; }; void SetRef( CookieRef* cref ) { m_cref = cref; } - void SetOwner( pthread_t owner ) { m_owner = owner; } + void SetOwner( pthread_t owner ); private: CookieID m_cid; CookieRef* m_cref; pthread_t m_owner; + int m_ownerCount; vector m_addrs; }; From ecef68747189b17b8ab14616ce875f88c037df73 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 17 Jun 2013 07:52:58 -0700 Subject: [PATCH 031/126] drop message, rather than asserting, if device id isn't in db. --- xwords4/relay/dbmgr.cpp | 56 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 7812285ca..b88c68b91 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -727,15 +727,16 @@ DBMgr::readArray( const char* const connName, const char* column, int arr[] ) / DevIDRelay DBMgr::getDevID( const char* connName, int hid ) { - DevIDRelay devID; + DevIDRelay devID = DEVID_NONE; const char* fmt = "SELECT devids[%d] FROM " GAMES_TABLE " WHERE connName='%s'"; string query; string_printf( query, fmt, hid, connName ); logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); PGresult* result = PQexec( getThreadConn(), query.c_str() ); - assert( 1 == PQntuples( result ) ); - devID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 ); + if ( 1 == PQntuples( result ) ) { + devID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 ); + } PQclear( result ); return devID; } @@ -839,32 +840,35 @@ DBMgr::StoreMessage( const char* const connName, int hid, const unsigned char* buf, int len ) { DevIDRelay devID = getDevID( connName, hid ); - - size_t newLen; - const char* fmt = "INSERT INTO " MSGS_TABLE - " (connname, hid, devid, token, %s, msglen)" - " VALUES( '%s', %d, %d, " - "(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), " - "%s'%s', %d)"; - - string query; - if ( m_useB64 ) { - gchar* b64 = g_base64_encode( buf, len ); - string_printf( query, fmt, "msg64", connName, hid, devID, hid, connName, - "", b64, len ); - g_free( b64 ); + if ( DEVID_NONE == devID ) { + logf( XW_LOGERROR, "%s: devid not found for connName=%s, hid=%d", __func__, connName, hid ); } else { - unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, - len, &newLen ); - assert( NULL != bytes ); + size_t newLen; + const char* fmt = "INSERT INTO " MSGS_TABLE + " (connname, hid, devid, token, %s, msglen)" + " VALUES( '%s', %d, %d, " + "(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), " + "%s'%s', %d)"; - string_printf( query, fmt, "msg", connName, hid, devID, hid, connName, - "E", bytes, len ); - PQfreemem( bytes ); - } + string query; + if ( m_useB64 ) { + gchar* b64 = g_base64_encode( buf, len ); + string_printf( query, fmt, "msg64", connName, hid, devID, hid, connName, + "", b64, len ); + g_free( b64 ); + } else { + unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, + len, &newLen ); + assert( NULL != bytes ); + + string_printf( query, fmt, "msg", connName, hid, devID, hid, connName, + "E", bytes, len ); + PQfreemem( bytes ); + } - logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); - execSql( query ); + logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); + execSql( query ); + } } void From 985f7bfea070a1b10dff7461c811f540a7659e89 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 19 Jun 2013 07:27:34 -0700 Subject: [PATCH 032/126] tweak: don't restart whole loop on unlikely random result --- xwords4/relay/dbmgr.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index b88c68b91..e205484ec 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -324,10 +324,10 @@ DBMgr::RegisterDevice( const DevID* host ) for ( success = false, ii = 0; !success; ++ii ) { assert( 10 > ii ); // better to check that we're looping BECAUSE // of uniqueness problem. - devID = (DevIDRelay)random(); - if ( DEVID_NONE == devID ) { - continue; - } + do { + devID = (DevIDRelay)random(); + } while ( DEVID_NONE != devID ); + const char* command = "INSERT INTO " DEVICES_TABLE " (id, devType, devid)" " VALUES( $1, $2, $3 )"; From 5bec10048bfa86470c238eb936d7146a36498f32 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 20 Jun 2013 06:45:51 -0700 Subject: [PATCH 033/126] add --clean-start option to speed testing --- xwords4/linux/scripts/discon_ok2.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 7abda93ab..12b4b9c19 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -1,6 +1,7 @@ #!/bin/bash set -u -e +LOGDIR=$(basename $0)_logs APP_NEW="" APP_NEW_PARAMS="" NGAMES="" @@ -39,6 +40,13 @@ declare -a APPS_OLD declare -a DICTS declare -A CHECKED_ROOMS +function cleanup() { + echo "cleaning everything up...." + rm -f $(dirname $0)/../../relay/xwrelay.log + rm -rf ${LOGDIR} + echo "delete from games;" | psql -q -t xwgames +} + function connName() { LOG=$1 grep 'got_connect_cmd: connName' $LOG | \ @@ -514,6 +522,7 @@ function getArg() { function usage() { [ $# -gt 0 ] && echo "Error: $1" >&2 echo "Usage: $(basename $0) \\" >&2 + echo " [--clean-start] \\" >&2 echo " [--game-dict ]* \\" >&2 echo " [--old-app &2 echo " [--new-app &2 @@ -538,6 +547,9 @@ function usage() { while [ "$#" -gt 0 ]; do case $1 in + --clean-start) + cleanup + ;; --num-games) NGAMES=$(getArg $*) shift @@ -620,7 +632,6 @@ done [ -n "$SEED" ] && RANDOM=$SEED [ -z "$ONEPER" -a $NROOMS -lt $NGAMES ] && usage "use --one-per if --num-rooms < --num-games" -LOGDIR=$(basename $0)_logs RESUME="" for FILE in $(ls $LOGDIR/*.{xwg,txt} 2>/dev/null); do if [ -e $FILE ]; then From 46bd4d00470c17f7801075810ea0c8e5d8b6eaf0 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 20 Jun 2013 07:07:56 -0700 Subject: [PATCH 034/126] inval tcp sockets in return addresses of packets waiting processing when they're closed to prevent attempting to write replies to the wrong device should the socket be reopened. --- xwords4/relay/addrinfo.h | 1 + xwords4/relay/tpool.cpp | 8 ++++-- xwords4/relay/udpqueue.cpp | 58 ++++++++++++++++++++++++++++++++++++++ xwords4/relay/udpqueue.h | 4 +++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/addrinfo.h b/xwords4/relay/addrinfo.h index 1c8d04d87..631e6f19f 100644 --- a/xwords4/relay/addrinfo.h +++ b/xwords4/relay/addrinfo.h @@ -57,6 +57,7 @@ class AddrInfo { void setIsTCP( bool val ) { m_isTCP = val; } bool isTCP() const { return m_isTCP; } /* later UDP will be here too */ int socket() const { assert(m_isValid); return m_socket; } + void invalSocket() { m_socket = -1; } ClientToken clientToken() const { assert(m_isValid); return m_clientToken; } struct in_addr sin_addr() const { return m_saddr.addr_in.sin_addr; } const struct sockaddr* sockaddr() const { assert(m_isValid); return &m_saddr.addr; } diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index 396c5a4b1..cf6ddb99d 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -186,6 +186,7 @@ XWThreadPool::CloseSocket( const AddrInfo* addr ) { /* bool do_interrupt = false; */ assert( addr->isTCP() ); + UdpQueue::get()->forgetSocket( addr ); if ( !RemoveSocket( addr ) ) { MutexLock ml( &m_queueMutex ); deque::iterator iter = m_queue.begin(); @@ -482,7 +483,8 @@ XWThreadPool::grab_elem_locked( QueuePr* prp ) for ( iter = m_queue.begin(); !found && iter != m_queue.end(); ++iter ) { int socket = iter->m_info.m_addr.socket(); /* If NOT found */ - if ( m_sockets_in_use.end() == m_sockets_in_use.find( socket ) ) { + if ( -1 != socket + && m_sockets_in_use.end() == m_sockets_in_use.find( socket ) ) { *prp = *iter; m_queue.erase( iter ); /* this was a double-free once! */ m_sockets_in_use.insert( socket ); @@ -513,10 +515,10 @@ XWThreadPool::print_in_use( void ) for ( iter = m_sockets_in_use.begin(); iter != m_sockets_in_use.end(); ++iter ) { - string_printf( str, "%d ", *iter ); + string_printf( str, "%d ", *iter ); } if ( 0 < str.size() ) { - logf( XW_LOGINFO, "Sockets in use: %s", str.c_str() ); + logf( XW_LOGINFO, "Sockets in use: %s", str.c_str() ); } } diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index 17eae630c..ab08b97a6 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -107,9 +107,56 @@ UdpQueue::handle( const AddrInfo* addr, unsigned char* buf, int len, utc->setID( id ); logf( XW_LOGINFO, "%s: enqueuing packet %d", __func__, id ); m_queue.push_back( utc ); + + int sock = addr->socket(); + map >::iterator iter = m_bySocket.find( sock ); + if ( iter == m_bySocket.end() ) { + logf( XW_LOGINFO, "%s: creating vector for socket %d", __func__, sock ); + vector vect; + vect.push_back( utc ); + m_bySocket.insert( pair >(sock, vect) ); + } else { + iter->second.push_back( utc ); + logf( XW_LOGINFO, "%s: now have %d packets for socket %d", + __func__, iter->second.size(), sock ); + } + pthread_cond_signal( &m_queueCondVar ); } +void +UdpQueue::forgetSocket( const AddrInfo* addr ) +{ + assert( addr->isTCP() ); + int sock = addr->socket(); + MutexLock ml( &m_queueMutex ); + + map >::iterator iter = m_bySocket.find( sock ); + if ( m_bySocket.end() != iter ) { + vector& vect = iter->second; + vector::iterator iter2; + for ( iter2 = vect.begin(); vect.end() != iter2; ++ iter2 ) { + UdpThreadClosure* utc = *iter2; + assert( -1 != utc->addr()->socket() ); + utc->invalSocket(); + logf( XW_LOGINFO, "%s: invalidating socket %d in packet %d", + __func__, sock, utc->getID() ); + // vect.erase( iter2 ); + } + vect.clear(); + } + + // deque::iterator iter; + // for ( iter = m_queue.begin(); iter != m_queue.end(); ++iter ) { + // const AddrInfo* addr = (*iter)->addr(); + // if ( sock == addr->socket() ) { + // logf( XW_LOGINFO, "%s: invalidating socket %d in packet %d", + // __func__, sock, (*iter)->getID() ); + // (*iter)->invalSocket(); + // } + // } +} + void* UdpQueue::thread_main() { @@ -120,6 +167,17 @@ UdpQueue::thread_main() } UdpThreadClosure* utc = m_queue.front(); m_queue.pop_front(); + + int sock = utc->addr()->socket(); + if ( -1 != sock ) { + map >::iterator iter = m_bySocket.find( sock ); + assert ( iter != m_bySocket.end() ); + vector& vect = iter->second; + assert( utc == *vect.begin() ); + vect.erase( vect.begin() ); + logf( XW_LOGINFO, "%s: %d packets remaining for socket %d", + __func__, vect.size(), sock ); + } pthread_mutex_unlock( &m_queueMutex ); utc->noteDequeued(); diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index a2ee730a8..544c47088 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -23,6 +23,7 @@ #include #include +#include #include "xwrelay_priv.h" #include "addrinfo.h" @@ -57,6 +58,7 @@ public: const QueueCallback cb() const { return m_cb; } void setID( int id ) { m_id = id; } int getID( void ) { return m_id; } + void invalSocket() { m_addr.invalSocket(); } private: unsigned char* m_buf; @@ -76,6 +78,7 @@ class UdpQueue { bool handle( const AddrInfo* addr, QueueCallback cb ); void handle( const AddrInfo* addr, unsigned char* buf, int len, QueueCallback cb ); + void forgetSocket( const AddrInfo* addr ); private: static void* thread_main_static( void* closure ); @@ -84,6 +87,7 @@ class UdpQueue { pthread_mutex_t m_queueMutex; pthread_cond_t m_queueCondVar; deque m_queue; + map > m_bySocket; int m_nextID; }; From 11d299606e38924d6ac20024996f1f3b4fa4a412 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 20 Jun 2013 07:10:09 -0700 Subject: [PATCH 035/126] test for socket == -1 (invalidated) before sending --- xwords4/relay/cref.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index bf61d8a70..470f6b3f2 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -859,20 +859,20 @@ CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr ) logf( XW_LOGVERBOSE0, "%s(dest=%d)", __func__, dest ); assert( dest > 0 && dest <= 4 ); - assert( -1 != addr->socket() ); - - for ( ; ; ) { - unsigned char buf[MAX_MSG_LEN]; - size_t buflen = sizeof(buf); - int msgID; - if ( !DBMgr::Get()->GetStoredMessage( ConnName(), dest, - buf, &buflen, &msgID ) ) { - break; + if ( -1 != addr->socket() ) { + for ( ; ; ) { + unsigned char buf[MAX_MSG_LEN]; + size_t buflen = sizeof(buf); + int msgID; + if ( !DBMgr::Get()->GetStoredMessage( ConnName(), dest, + buf, &buflen, &msgID ) ) { + break; + } + if ( ! send_with_length( addr, dest, buf, buflen, true ) ) { + break; + } + DBMgr::Get()->RemoveStoredMessages( &msgID, 1 ); } - if ( ! send_with_length( addr, dest, buf, buflen, true ) ) { - break; - } - DBMgr::Get()->RemoveStoredMessages( &msgID, 1 ); } } /* send_stored_messages */ From 6446819466e1ba225273dfe504162d39b0925471 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 05:41:23 -0700 Subject: [PATCH 036/126] use nice to give the relay a fighting chance --- xwords4/linux/scripts/discon_ok2.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 12b4b9c19..652005dcf 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -449,6 +449,7 @@ run_cmds() { try_upgrade $KEY launch $KEY & PID=$! + renice +1 $PID >/dev/null PIDS[$KEY]=$PID ROOM_PIDS[$ROOM]=$PID MINEND[$KEY]=$(($NOW + $MINRUN)) From 2a35fac1e89a5f6107e1f726e09b6e666e5fc4d5 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 06:05:26 -0700 Subject: [PATCH 037/126] rather than invalidating socket in AddrRec when it's closed, add a timestamp set when it's opened. Older copies with the same socket can be tested against the cannonical copy maintained by tpool and sending avoided when the timestamp shows the endpoint has likely changed. Change tpool's list of sockets to a map for faster lookup, and get rid of similar structure in udpqueue. --- xwords4/relay/addrinfo.cpp | 24 +++++++++++++++ xwords4/relay/addrinfo.h | 15 ++++----- xwords4/relay/cref.cpp | 4 ++- xwords4/relay/tpool.cpp | 62 ++++++++++++++++++++++---------------- xwords4/relay/tpool.h | 4 ++- xwords4/relay/udpqueue.cpp | 56 ---------------------------------- xwords4/relay/udpqueue.h | 3 +- xwords4/relay/xwrelay.cpp | 62 ++++++++++++-------------------------- 8 files changed, 93 insertions(+), 137 deletions(-) diff --git a/xwords4/relay/addrinfo.cpp b/xwords4/relay/addrinfo.cpp index e61f3595d..0fbd607d2 100644 --- a/xwords4/relay/addrinfo.cpp +++ b/xwords4/relay/addrinfo.cpp @@ -21,8 +21,27 @@ #include #include +#include #include "addrinfo.h" +#include "xwrelay_priv.h" +#include "tpool.h" + +void +AddrInfo::construct( int socket, const AddrUnion* saddr, bool isTCP ) +{ + memset( this, 0, sizeof(*this) ); + + struct timespec tp; + clock_gettime( CLOCK_MONOTONIC, &tp ); + m_created = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000); + logf( XW_LOGINFO, "%s: m_created for socket %d: 0x%lx", __func__, socket, m_created ); + + m_socket = socket; + m_isTCP = isTCP; + memcpy( &m_saddr, saddr, sizeof(m_saddr) ); + m_isValid = true; +} bool AddrInfo::equals( const AddrInfo& other ) const @@ -31,6 +50,11 @@ AddrInfo::equals( const AddrInfo& other ) const if ( equal ) { if ( isTCP() ) { equal = m_socket == other.m_socket; + if ( equal && created() != other.created() ) { + logf( XW_LOGINFO, "%s: rejecting on time mismatch (%lx vs %lx)", + __func__, created(), other.created() ); + equal = false; + } } else { // assert( m_socket == other.m_socket ); /* both same UDP socket */ /* what does equal mean on udp addresses? Same host, or same host AND game */ diff --git a/xwords4/relay/addrinfo.h b/xwords4/relay/addrinfo.h index 631e6f19f..7cd7db95f 100644 --- a/xwords4/relay/addrinfo.h +++ b/xwords4/relay/addrinfo.h @@ -57,23 +57,19 @@ class AddrInfo { void setIsTCP( bool val ) { m_isTCP = val; } bool isTCP() const { return m_isTCP; } /* later UDP will be here too */ int socket() const { assert(m_isValid); return m_socket; } - void invalSocket() { m_socket = -1; } ClientToken clientToken() const { assert(m_isValid); return m_clientToken; } struct in_addr sin_addr() const { return m_saddr.addr_in.sin_addr; } const struct sockaddr* sockaddr() const { assert(m_isValid); return &m_saddr.addr; } const AddrUnion* saddr() const { assert(m_isValid); return &m_saddr; } + uint32_t created() const { return m_created; } + bool isCurrent() const { + return XWThreadPool::GetTPool()->IsCurrent( this ); + } bool equals( const AddrInfo& other ) const; private: - void construct( int socket, const AddrUnion* saddr, bool isTCP ) { - memset( this, 0, sizeof(*this) ); - - m_socket = socket; - m_isTCP = isTCP; - memcpy( &m_saddr, saddr, sizeof(m_saddr) ); - m_isValid = true; - } + void construct( int socket, const AddrUnion* saddr, bool isTCP ); // AddrInfo& operator=(const AddrInfo&); // Prevent assignment int m_socket; @@ -81,6 +77,7 @@ class AddrInfo { bool m_isValid; ClientToken m_clientToken; /* must be 32 bit */ AddrUnion m_saddr; + uint32_t m_created; /* microseconds since boot, from clock_gettime() */ }; #endif diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index 470f6b3f2..510e3878f 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -859,7 +859,7 @@ CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr ) logf( XW_LOGVERBOSE0, "%s(dest=%d)", __func__, dest ); assert( dest > 0 && dest <= 4 ); - if ( -1 != addr->socket() ) { + if ( addr->isCurrent() ) { for ( ; ; ) { unsigned char buf[MAX_MSG_LEN]; size_t buflen = sizeof(buf); @@ -936,6 +936,8 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp, { RWWriteLock rwl( &m_socketsRWLock ); HostRec hr( hostid, &evt->addr, nPlayersH, seed, !reconn ); + logf( XW_LOGINFO, "%s: adding socket rec with ts %lx", __func__, + evt->addr.created() ); m_sockets.push_back( hr ); } diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index cf6ddb99d..a88276f29 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -118,13 +118,14 @@ void XWThreadPool::AddSocket( SockType stype, QueueCallback proc, const AddrInfo* from ) { { + int sock = from->socket(); RWWriteLock ml( &m_activeSocketsRWLock ); SockInfo si; si.m_type = stype; si.m_proc = proc; si.m_addr = *from; - m_activeSockets.push_back( si ); - logf( XW_LOGINFO, "%s: %d sockets active", __func__, + m_activeSockets.insert( pair( sock, si ) ); + logf( XW_LOGINFO, "%s(sock=%d): %d sockets active", __func__, sock, m_activeSockets.size() ); } interrupt_poll(); @@ -138,19 +139,11 @@ XWThreadPool::SocketFound( const AddrInfo* addr ) { RWWriteLock ml( &m_activeSocketsRWLock ); - logf( XW_LOGINFO, "%s: START: %d sockets active", __func__, - m_activeSockets.size() ); - - vector::iterator iter; - for ( iter = m_activeSockets.begin(); - iter != m_activeSockets.end(); ++iter ) { - if ( iter->m_addr.equals( *addr ) ) { - found = true; - break; - } + map::iterator iter = m_activeSockets.find( addr->socket() ); + if ( m_activeSockets.end() != iter + && iter->second.m_addr.equals( *addr ) ) { + found = true; } - logf( XW_LOGINFO, "%s: AFTER: %d sockets active", __func__, - m_activeSockets.size() ); } return found; } @@ -166,14 +159,10 @@ XWThreadPool::RemoveSocket( const AddrInfo* addr ) logf( XW_LOGINFO, "%s: START: %d sockets active", __func__, m_activeSockets.size() ); - vector::iterator iter; - for ( iter = m_activeSockets.begin(); - iter != m_activeSockets.end(); ++iter ) { - if ( iter->m_addr.equals( *addr ) ) { - m_activeSockets.erase( iter ); - found = true; - break; - } + map::iterator iter = m_activeSockets.find( addr->socket() ); + if ( m_activeSockets.end() != iter && iter->second.m_addr.equals( *addr ) ) { + m_activeSockets.erase( iter ); + found = true; } logf( XW_LOGINFO, "%s: AFTER: %d sockets active", __func__, m_activeSockets.size() ); @@ -186,7 +175,6 @@ XWThreadPool::CloseSocket( const AddrInfo* addr ) { /* bool do_interrupt = false; */ assert( addr->isTCP() ); - UdpQueue::get()->forgetSocket( addr ); if ( !RemoveSocket( addr ) ) { MutexLock ml( &m_queueMutex ); deque::iterator iter = m_queue.begin(); @@ -222,6 +210,28 @@ XWThreadPool::EnqueueKill( const AddrInfo* addr, const char* const why ) } } +// return true if the addr passed in has a timestamp >= what we have as the +// creation time of the now-open socket. If the socket isn't open, return false. +bool +XWThreadPool::IsCurrent( const AddrInfo* addr ) +{ + bool result = false; + bool sockFound = false; // for debugging + int sock = addr->socket(); + if ( -1 != sock ) { + RWReadLock ml( &m_activeSocketsRWLock ); + map::const_iterator iter = m_activeSockets.find( sock ); + if ( iter != m_activeSockets.end() ) { + assert( !sockFound ); + sockFound = true; + result = iter->second.m_addr.created() <= addr->created(); + logf( XW_LOGINFO, "%s(sock=%d)=>%d (%lx vs %lx)", __func__, sock, result, + iter->second.m_addr.created(), addr->created() ); + } + } + return result; +} + // bool // XWThreadPool::get_process_packet( SockType stype, QueueCallback proc, const AddrInfo* addr ) // { @@ -359,11 +369,11 @@ XWThreadPool::real_listener() #endif ++curfd; - vector::iterator iter; + map::iterator iter; for ( iter = m_activeSockets.begin(); iter != m_activeSockets.end(); ++iter ) { - fds[curfd].fd = iter->m_addr.socket(); - sinfos[curfd] = *iter; + fds[curfd].fd = iter->first; + sinfos[curfd] = iter->second; fds[curfd].events = flags; #ifdef LOG_POLL if ( logCapacity > logLen ) { diff --git a/xwords4/relay/tpool.h b/xwords4/relay/tpool.h index 9ae0b1359..ae65ecb46 100644 --- a/xwords4/relay/tpool.h +++ b/xwords4/relay/tpool.h @@ -68,6 +68,8 @@ class XWThreadPool { void EnqueueKill( const AddrInfo* addr, const char* const why ); + bool IsCurrent( const AddrInfo* addr ); + private: typedef enum { Q_READ, Q_KILL } QAction; typedef struct { QAction m_act; SockInfo m_info; } QueuePr; @@ -94,7 +96,7 @@ class XWThreadPool { static void* listener_main( void* closure ); /* Sockets main thread listens on */ - vectorm_activeSockets; + mapm_activeSockets; pthread_rwlock_t m_activeSocketsRWLock; /* Sockets waiting for a thread to read 'em */ diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index ab08b97a6..c4a89eac3 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -108,55 +108,9 @@ UdpQueue::handle( const AddrInfo* addr, unsigned char* buf, int len, logf( XW_LOGINFO, "%s: enqueuing packet %d", __func__, id ); m_queue.push_back( utc ); - int sock = addr->socket(); - map >::iterator iter = m_bySocket.find( sock ); - if ( iter == m_bySocket.end() ) { - logf( XW_LOGINFO, "%s: creating vector for socket %d", __func__, sock ); - vector vect; - vect.push_back( utc ); - m_bySocket.insert( pair >(sock, vect) ); - } else { - iter->second.push_back( utc ); - logf( XW_LOGINFO, "%s: now have %d packets for socket %d", - __func__, iter->second.size(), sock ); - } - pthread_cond_signal( &m_queueCondVar ); } -void -UdpQueue::forgetSocket( const AddrInfo* addr ) -{ - assert( addr->isTCP() ); - int sock = addr->socket(); - MutexLock ml( &m_queueMutex ); - - map >::iterator iter = m_bySocket.find( sock ); - if ( m_bySocket.end() != iter ) { - vector& vect = iter->second; - vector::iterator iter2; - for ( iter2 = vect.begin(); vect.end() != iter2; ++ iter2 ) { - UdpThreadClosure* utc = *iter2; - assert( -1 != utc->addr()->socket() ); - utc->invalSocket(); - logf( XW_LOGINFO, "%s: invalidating socket %d in packet %d", - __func__, sock, utc->getID() ); - // vect.erase( iter2 ); - } - vect.clear(); - } - - // deque::iterator iter; - // for ( iter = m_queue.begin(); iter != m_queue.end(); ++iter ) { - // const AddrInfo* addr = (*iter)->addr(); - // if ( sock == addr->socket() ) { - // logf( XW_LOGINFO, "%s: invalidating socket %d in packet %d", - // __func__, sock, (*iter)->getID() ); - // (*iter)->invalSocket(); - // } - // } -} - void* UdpQueue::thread_main() { @@ -168,16 +122,6 @@ UdpQueue::thread_main() UdpThreadClosure* utc = m_queue.front(); m_queue.pop_front(); - int sock = utc->addr()->socket(); - if ( -1 != sock ) { - map >::iterator iter = m_bySocket.find( sock ); - assert ( iter != m_bySocket.end() ); - vector& vect = iter->second; - assert( utc == *vect.begin() ); - vect.erase( vect.begin() ); - logf( XW_LOGINFO, "%s: %d packets remaining for socket %d", - __func__, vect.size(), sock ); - } pthread_mutex_unlock( &m_queueMutex ); utc->noteDequeued(); diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index 544c47088..890d9d234 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -58,7 +58,6 @@ public: const QueueCallback cb() const { return m_cb; } void setID( int id ) { m_id = id; } int getID( void ) { return m_id; } - void invalSocket() { m_addr.invalSocket(); } private: unsigned char* m_buf; @@ -87,7 +86,7 @@ class UdpQueue { pthread_mutex_t m_queueMutex; pthread_cond_t m_queueCondVar; deque m_queue; - map > m_bySocket; + // map > m_bySocket; int m_nextID; }; diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 41e4e635b..64f78a0ce 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -448,23 +448,32 @@ send_with_length_unsafe( const AddrInfo* addr, const unsigned char* buf, { assert( !!addr ); bool ok = false; - int socket = addr->socket(); if ( addr->isTCP() ) { - unsigned short len = htons( bufLen ); - ssize_t nSent = send( socket, &len, 2, 0 ); - if ( nSent == 2 ) { - nSent = send( socket, buf, bufLen, 0 ); - if ( nSent == ssize_t(bufLen) ) { - logf( XW_LOGINFO, "sent %d bytes on socket %d", nSent, socket ); - ok = true; + if ( addr->isCurrent() ) { + int socket = addr->socket(); + unsigned short len = htons( bufLen ); + ssize_t nSent = send( socket, &len, 2, 0 ); + if ( nSent == 2 ) { + nSent = send( socket, buf, bufLen, 0 ); + if ( nSent == ssize_t(bufLen) ) { + logf( XW_LOGINFO, "sent %d bytes on socket %d", nSent, socket ); + ok = true; + } else { + logf( XW_LOGERROR, "%s: send failed: %s (errno=%d)", __func__, + strerror(errno), errno ); + } } + } else { + logf( XW_LOGINFO, "%s: dropping packet: socket %d reused", + __func__, socket ); } } else { AddrInfo::ClientToken clientToken = addr->clientToken(); assert( 0 != clientToken ); clientToken = htonl(clientToken); const struct sockaddr* saddr = addr->sockaddr(); + int socket = addr->socket(); assert( g_udpsock == socket || socket == -1 ); if ( -1 == socket ) { socket = g_udpsock; @@ -887,37 +896,6 @@ handlePipe( int sig ) logf( XW_LOGINFO, "%s", __func__ ); } -int -read_packet( int sock, unsigned char* buf, int buflen ) -{ - int result = -1; - ssize_t nread; - unsigned short msgLen; - nread = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL ); - if ( 0 == nread ) { - logf( XW_LOGINFO, "%s: recv => 0: remote closed", __func__ ); - } else if ( nread != sizeof(msgLen) ) { - logf( XW_LOGERROR, "%s: first recv => %d: %s", __func__, - nread, strerror(errno) ); - } else { - msgLen = ntohs( msgLen ); - if ( msgLen >= buflen ) { - logf( XW_LOGERROR, "%s: buf too small; need %d but have %d", - __func__, msgLen, buflen ); - } else { - nread = recv( sock, buf, msgLen, MSG_WAITALL ); - if ( nread == msgLen ) { - result = nread; - } else { - logf( XW_LOGERROR, "%s: second recv failed: %s", __func__, - strerror(errno) ); - } - } - } - - return result; -} /* read_packet */ - static void pushShort( vector& out, unsigned short num ) { @@ -1104,7 +1082,7 @@ handleProxyMsgs( int sock, const AddrInfo* addr, const unsigned char* bufp, if ( getNetShort( &bufp, end, &len ) ) { if ( handlePutMessage( scr, hid, addr, len, &bufp, end ) ) { continue; - } + } } break; } @@ -1427,7 +1405,7 @@ udp_thread_proc( UdpThreadClosure* utc ) } static void -handle_udp_packet( int udpsock ) +read_udp_packet( int udpsock ) { unsigned char buf[MAX_MSG_LEN]; AddrInfo::AddrUnion saddr; @@ -1951,7 +1929,7 @@ main( int argc, char** argv ) if ( -1 != g_udpsock && FD_ISSET( g_udpsock, &rfds ) ) { // This will need to be done in a separate thread, or pushed // to the existing thread pool - handle_udp_packet( g_udpsock ); + read_udp_packet( g_udpsock ); --retval; } #ifdef DO_HTTP From 8b8d22a7bad136aad69a67354d0e086db9dd62bc Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 06:14:10 -0700 Subject: [PATCH 038/126] fix compile error: move function back to being inline since header can't be readily included from .h file --- xwords4/relay/addrinfo.cpp | 8 ++++++++ xwords4/relay/addrinfo.h | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/addrinfo.cpp b/xwords4/relay/addrinfo.cpp index 0fbd607d2..17eba8b68 100644 --- a/xwords4/relay/addrinfo.cpp +++ b/xwords4/relay/addrinfo.cpp @@ -43,6 +43,14 @@ AddrInfo::construct( int socket, const AddrUnion* saddr, bool isTCP ) m_isValid = true; } +bool +AddrInfo::isCurrent() const +{ + return XWThreadPool::GetTPool()->IsCurrent( this ); +} + + + bool AddrInfo::equals( const AddrInfo& other ) const { diff --git a/xwords4/relay/addrinfo.h b/xwords4/relay/addrinfo.h index 7cd7db95f..7a4282dfe 100644 --- a/xwords4/relay/addrinfo.h +++ b/xwords4/relay/addrinfo.h @@ -62,9 +62,7 @@ class AddrInfo { const struct sockaddr* sockaddr() const { assert(m_isValid); return &m_saddr.addr; } const AddrUnion* saddr() const { assert(m_isValid); return &m_saddr; } uint32_t created() const { return m_created; } - bool isCurrent() const { - return XWThreadPool::GetTPool()->IsCurrent( this ); - } + bool isCurrent() const; bool equals( const AddrInfo& other ) const; From 5e22508ff7f0a29f117e2487fd97e795eb8cfc0e Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 06:58:20 -0700 Subject: [PATCH 039/126] kill existing xwords instances as part of --clean-start --- xwords4/linux/scripts/discon_ok2.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 652005dcf..678c8d2b9 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -3,6 +3,7 @@ set -u -e LOGDIR=$(basename $0)_logs APP_NEW="" +DO_CLEAN="" APP_NEW_PARAMS="" NGAMES="" UPGRADE_ODDS="" @@ -41,6 +42,12 @@ declare -a DICTS declare -A CHECKED_ROOMS function cleanup() { + APP="$(basename $APP_NEW)" + while pidof $APP; do + echo "killing existing $APP instances..." + killall -9 $APP + sleep 1 + done echo "cleaning everything up...." rm -f $(dirname $0)/../../relay/xwrelay.log rm -rf ${LOGDIR} @@ -549,7 +556,7 @@ function usage() { while [ "$#" -gt 0 ]; do case $1 in --clean-start) - cleanup + DO_CLEAN=1 ;; --num-games) NGAMES=$(getArg $*) @@ -633,6 +640,8 @@ done [ -n "$SEED" ] && RANDOM=$SEED [ -z "$ONEPER" -a $NROOMS -lt $NGAMES ] && usage "use --one-per if --num-rooms < --num-games" +[ -n "$DO_CLEAN" ] && cleanup + RESUME="" for FILE in $(ls $LOGDIR/*.{xwg,txt} 2>/dev/null); do if [ -e $FILE ]; then From c587848349b054bf0a6123331a26c4919fc46caa Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 06:59:44 -0700 Subject: [PATCH 040/126] cleanup --- xwords4/relay/tpool.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index a88276f29..7bb5eaf15 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -232,30 +232,6 @@ XWThreadPool::IsCurrent( const AddrInfo* addr ) return result; } -// bool -// XWThreadPool::get_process_packet( SockType stype, QueueCallback proc, const AddrInfo* addr ) -// { -// bool success = false; -// short packetSize; -// assert( sizeof(packetSize) == 2 ); - -// // Fix this to return an allocated buffer -// unsigned char buf[MAX_MSG_LEN+1]; -// int nRead = read_packet( addr->socket(), buf, sizeof(buf) ); -// if ( nRead < 0 ) { -// EnqueueKill( addr, "bad packet" ); -// } else if ( STYPE_PROXY == stype && NULL != proc ) { -// buf[nRead] = '\0'; -// UdpQueue::get()->handle( addr, buf, nRead+1, proc ); -// } else if ( STYPE_GAME == stype && NULL != proc ) { -// UdpQueue::get()->handle( addr, buf, nRead, proc ); -// success = true; -// } else { -// assert(0); -// } -// return success; -// } /* get_process_packet */ - void* XWThreadPool::tpool_main( void* closure ) { From b1ef09625e04bd896a408aafcca5ae6b932db33e Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 07:25:41 -0700 Subject: [PATCH 041/126] pair delete[] with new[] --- xwords4/relay/udpqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index 890d9d234..0aa04afa0 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -47,7 +47,7 @@ public: memcpy( m_buf, buf, len ); } - ~UdpThreadClosure() { delete m_buf; } + ~UdpThreadClosure() { delete[] m_buf; } const unsigned char* buf() const { return m_buf; } int len() const { return m_len; } From 6027c94a438549a1a20b98004f145b2362d307a5 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 21 Jun 2013 07:26:11 -0700 Subject: [PATCH 042/126] don't pass uninitialized variables to logf --- xwords4/relay/dbmgr.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index e205484ec..c08622dfd 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -674,8 +674,15 @@ DBMgr::TokenFor( const char* const connName, int hid, DevIDRelay* devid, } } PQclear( result ); - logf( XW_LOGINFO, "%s(%s,%d)=>%s (%d, %d)", __func__, connName, hid, - (found?"true":"false"), *devid, *token ); + + + if ( found ) { + logf( XW_LOGINFO, "%s(%s,%d)=>true (%d, %d)", __func__, connName, hid, + *devid, *token ); + } else { + logf( XW_LOGINFO, "%s(%s,%d)=>false", __func__, connName, hid ); + } + return found; } From 6d22c3ab865f5a798056cf8b3882ad5acec8d58e Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:51:29 -0700 Subject: [PATCH 043/126] tweak logging --- xwords4/common/comms.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 01f597206..7ea6a715c 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1386,7 +1386,8 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream, __func__, comms->r.connName, connName ); } XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) ); - XP_LOGF( "%s: connName: \"%s\"", __func__, connName ); + XP_LOGF( "%s: connName: \"%s\" (reconnect=%d)", __func__, connName, + reconnected ); } #else stringFromStreamHere( stream, comms->r.connName, From 5d20ef872ea0b7076e07ca07b0e9915cff89ea7f Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:53:32 -0700 Subject: [PATCH 044/126] cleanup --- xwords4/relay/addrinfo.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/xwords4/relay/addrinfo.cpp b/xwords4/relay/addrinfo.cpp index 17eba8b68..504b94ebf 100644 --- a/xwords4/relay/addrinfo.cpp +++ b/xwords4/relay/addrinfo.cpp @@ -49,8 +49,6 @@ AddrInfo::isCurrent() const return XWThreadPool::GetTPool()->IsCurrent( this ); } - - bool AddrInfo::equals( const AddrInfo& other ) const { From 25bf0e7191eaf52132701f324898132ed0cd3f89 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:53:54 -0700 Subject: [PATCH 045/126] assertion: should never have two recs for same socket --- xwords4/relay/cidlock.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xwords4/relay/cidlock.cpp b/xwords4/relay/cidlock.cpp index b9ab983a4..bb9fa7f98 100644 --- a/xwords4/relay/cidlock.cpp +++ b/xwords4/relay/cidlock.cpp @@ -1,4 +1,4 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* -*- -*- */ /* * Copyright 2005-2011 by Eric House (xwords@eehouse.org). All rights @@ -148,12 +148,13 @@ CidLock::ClaimSocket( const AddrInfo* addr ) vector::const_iterator iter2; for ( iter2 = addrs.begin(); iter2 != addrs.end(); ++iter2 ) { if ( iter2->equals(*addr) ) { + assert( !info ); // I hit this -- twice!!!! if ( 0 == iter->second->GetOwner() ) { info = iter->second; info->SetOwner( pthread_self() ); PRINT_CLAIMED(); } - break; + // break; } } } From c1cc11866f69d5eee9e174d2c7ef4fcedfc345e8 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:54:32 -0700 Subject: [PATCH 046/126] tweak logging --- xwords4/relay/cref.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index 510e3878f..2061fdd56 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -422,8 +422,8 @@ CookieRef::removeSocket( const AddrInfo* addr ) if ( iter->m_addr.equals( *addr ) ) { if ( iter->m_ackPending ) { logf( XW_LOGINFO, - "Never got ack; removing hid %d from DB", - iter->m_hostID ); + "%s: Never got ack; removing hid %d from DB", + __func__, iter->m_hostID ); DBMgr::Get()->RmDeviceByHid( ConnName(), iter->m_hostID ); m_nPlayersHere -= iter->m_nPlayersH; @@ -860,18 +860,17 @@ CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr ) assert( dest > 0 && dest <= 4 ); if ( addr->isCurrent() ) { + DBMgr* dbmgr = DBMgr::Get(); + const char* cname = ConnName(); for ( ; ; ) { unsigned char buf[MAX_MSG_LEN]; size_t buflen = sizeof(buf); int msgID; - if ( !DBMgr::Get()->GetStoredMessage( ConnName(), dest, - buf, &buflen, &msgID ) ) { + if ( !dbmgr->GetStoredMessage( cname, dest, buf, &buflen, &msgID ) + || ! send_with_length( addr, dest, buf, buflen, true ) ) { break; } - if ( ! send_with_length( addr, dest, buf, buflen, true ) ) { - break; - } - DBMgr::Get()->RemoveStoredMessages( &msgID, 1 ); + dbmgr->RemoveStoredMessages( &msgID, 1 ); } } } /* send_stored_messages */ @@ -1463,7 +1462,7 @@ CookieRef::logf( XW_LogLevel level, const char* format, ... ) char buf[256]; int len; - len = snprintf( buf, sizeof(buf), "cid:%d ", m_cid ); + len = snprintf( buf, sizeof(buf), "cid:%d(%s) ", m_cid, m_connName.c_str() ); va_list ap; va_start( ap, format ); From 07d6851a269ae24d83509277b9c98414f939d6e6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:55:38 -0700 Subject: [PATCH 047/126] logging changes for debugging -- main not belong in main branch --- xwords4/relay/dbmgr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index c08622dfd..07cda2ed2 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -131,12 +131,14 @@ DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen, const char* fmt = "SELECT cid, room, lang, nTotal, nPerDevice, dead FROM " GAMES_TABLE " WHERE connName = '%s'" - " LIMIT 1"; + // " LIMIT 1" + ; string query; string_printf( query, fmt, connName ); logf( XW_LOGINFO, "query: %s", query.c_str() ); PGresult* result = PQexec( getThreadConn(), query.c_str() ); + assert( 1 >= PQntuples( result ) ); if ( 1 == PQntuples( result ) ) { cid = atoi( PQgetvalue( result, 0, 0 ) ); snprintf( cookieBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) ); @@ -390,6 +392,7 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion, break; } } + logf( XW_LOGINFO, "%s: set newID = %d", __func__, newID ); } assert( newID <= 4 ); @@ -727,6 +730,7 @@ DBMgr::readArray( const char* const connName, const char* column, int arr[] ) / PGresult* result = PQexec( getThreadConn(), query.c_str() ); assert( 1 == PQntuples( result ) ); const char* arrStr = PQgetvalue( result, 0, 0 ); + logf( XW_LOGINFO, "%s: arrStr=\"%s\"", __func__, arrStr ); sscanf( arrStr, "{%d,%d,%d,%d}", &arr[0], &arr[1], &arr[2], &arr[3] ); PQclear( result ); } From acd7c8519fb86c951a4deb26b97e3ed407b74cbf Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 05:56:27 -0700 Subject: [PATCH 048/126] give timers ids so can be tracked in logs --- xwords4/relay/timermgr.cpp | 15 +++++++++++---- xwords4/relay/timermgr.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/xwords4/relay/timermgr.cpp b/xwords4/relay/timermgr.cpp index 41d434300..b1438e7c8 100644 --- a/xwords4/relay/timermgr.cpp +++ b/xwords4/relay/timermgr.cpp @@ -29,6 +29,7 @@ TimerMgr::TimerMgr() : m_nextFireTime(0) + ,m_nextID(0) { pthread_mutex_init( &m_timersMutex, NULL ); } @@ -55,6 +56,7 @@ TimerMgr::SetTimer( time_t inSeconds, TimerProc proc, void* closure, ti.interval = interval; MutexLock ml( &m_timersMutex ); + ti.id = ++m_nextID; if ( getTimer( proc, closure ) ) { logf( XW_LOGINFO, "%s: clearing old timer", __func__ ); @@ -145,6 +147,7 @@ TimerMgr::FireElapsedTimers() vector procs; vector closures; + vector ids; { MutexLock ml( &m_timersMutex ); /* loop until we get through without firing a single one. Only fire one @@ -157,6 +160,7 @@ TimerMgr::FireElapsedTimers() procs.push_back(tip->proc); closures.push_back(tip->closure); + ids.push_back(tip->id); if ( tip->interval ) { tip->when += tip->interval; @@ -167,10 +171,12 @@ TimerMgr::FireElapsedTimers() } } - vector::iterator iter1 = procs.begin(); - vector::iterator iter2 = closures.begin(); - while ( iter1 != procs.end() ) { - (*iter1++)(*iter2++); + vector::const_iterator procs_iter = procs.begin(); + vector::const_iterator closures_iter = closures.begin(); + vector::const_iterator ids_iter = ids.begin(); + while ( procs_iter != procs.end() ) { + logf( XW_LOGINFO, "%s: firing timer id=%d", __func__, *ids_iter++ ); + (*procs_iter++)(*closures_iter++); } MutexLock ml( &m_timersMutex ); @@ -184,6 +190,7 @@ TimerMgr::clearTimerImpl( TimerProc proc, void* closure ) for ( iter = m_timers.begin(); iter != m_timers.end(); ++iter ) { TimerInfo* tip = &(*iter); if ( tip->proc == proc && tip->closure == closure ) { + logf( XW_LOGINFO, "clearing timer id=%d", tip->id ); m_timers.erase(iter); break; } diff --git a/xwords4/relay/timermgr.h b/xwords4/relay/timermgr.h index 841833b71..9236da330 100644 --- a/xwords4/relay/timermgr.h +++ b/xwords4/relay/timermgr.h @@ -51,6 +51,7 @@ class TimerMgr { void* closure; time_t when; int interval; + uint32_t id; } TimerInfo; @@ -65,6 +66,7 @@ class TimerMgr { list m_timers; time_t m_nextFireTime; + uint32_t m_nextID; }; #endif From 5dadbfdad3319823a00bb365c6d6e185f6953b31 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 06:01:42 -0700 Subject: [PATCH 049/126] drop messages without enough delivery info --- xwords4/relay/xwrelay.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 64f78a0ce..33bc9a2cb 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -704,14 +704,15 @@ forwardMessage( const unsigned char* buf, int buflen, const AddrInfo* addr ) if ( getNetShort( &bufp, end, &cookieID ) && getNetByte( &bufp, end, &src ) - && getNetByte( &bufp, end, &dest ) ) { - logf( XW_LOGINFO, "cookieID = %d", cookieID ); + && getNetByte( &bufp, end, &dest ) + && 0 < src && 0 < dest ) { if ( COOKIE_ID_NONE == cookieID ) { SafeCref scr( addr ); success = scr.Forward( src, addr, dest, buf, buflen ); } else { - SafeCref scr( cookieID ); /* won't work if not allcon; will be 0 */ + /* won't work if not allcon; will be 0 */ + SafeCref scr( cookieID /*, true*/ ); success = scr.Forward( src, addr, dest, buf, buflen ); } } @@ -1478,7 +1479,7 @@ enable_keepalive( int sock ) int optval = 1; if ( 0 > setsockopt( sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof( optval ) ) ) { - logf( XW_LOGERROR, "setsockopt(SO_KEEPALIVE)=>%d (%s)", errno, + logf( XW_LOGERROR, "setsockopt(sock=%d, SO_KEEPALIVE)=>%d (%s)", sock, errno, strerror(errno) ); assert( 0 ); } From d14688a6a0b16b808b542505da6bb15cc4b9ba2f Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 22 Jun 2013 22:24:18 -0700 Subject: [PATCH 050/126] fix almost-infinite loop; assert no dup devids --- xwords4/relay/dbmgr.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 07cda2ed2..16df28ba9 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -328,7 +328,7 @@ DBMgr::RegisterDevice( const DevID* host ) // of uniqueness problem. do { devID = (DevIDRelay)random(); - } while ( DEVID_NONE != devID ); + } while ( DEVID_NONE == devID ); const char* command = "INSERT INTO " DEVICES_TABLE " (id, devType, devid)" @@ -767,15 +767,17 @@ DBMgr::getDevID( const DevID* devID ) string_printf( query, fmt, cur ); } } else { - const char* fmt = "SELECT id FROM " DEVICES_TABLE " WHERE devtype=%d and devid = '%s'"; + const char* fmt = "SELECT id FROM " DEVICES_TABLE + " WHERE devtype=%d and devid = '%s'"; string_printf( query, fmt, devIDType, devID->m_devIDString.c_str() ); } if ( 0 < query.size() ) { logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); PGresult* result = PQexec( getThreadConn(), query.c_str() ); - assert( 1 >= PQntuples( result ) ); - if ( 1 == PQntuples( result ) ) { + int nTuples = PQntuples( result ); + assert( 1 >= nTuples ); + if ( 1 == nTuples ) { rDevID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 ); } PQclear( result ); @@ -852,7 +854,8 @@ DBMgr::StoreMessage( const char* const connName, int hid, { DevIDRelay devID = getDevID( connName, hid ); if ( DEVID_NONE == devID ) { - logf( XW_LOGERROR, "%s: devid not found for connName=%s, hid=%d", __func__, connName, hid ); + logf( XW_LOGERROR, "%s: aborting: devid not found for connName=%s, hid=%d", + __func__, connName, hid ); } else { size_t newLen; const char* fmt = "INSERT INTO " MSGS_TABLE From ed16437f9a14d9d3266768931a4d3a89eda1a091 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 23 Jun 2013 19:07:45 -0700 Subject: [PATCH 051/126] don't refuse to store message when devid isn't available --- xwords4/relay/dbmgr.cpp | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 16df28ba9..f87062914 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -854,35 +854,35 @@ DBMgr::StoreMessage( const char* const connName, int hid, { DevIDRelay devID = getDevID( connName, hid ); if ( DEVID_NONE == devID ) { - logf( XW_LOGERROR, "%s: aborting: devid not found for connName=%s, hid=%d", + logf( XW_LOGERROR, "%s: warning: devid not found for connName=%s, hid=%d", __func__, connName, hid ); - } else { - size_t newLen; - const char* fmt = "INSERT INTO " MSGS_TABLE - " (connname, hid, devid, token, %s, msglen)" - " VALUES( '%s', %d, %d, " - "(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), " - "%s'%s', %d)"; - - string query; - if ( m_useB64 ) { - gchar* b64 = g_base64_encode( buf, len ); - string_printf( query, fmt, "msg64", connName, hid, devID, hid, connName, - "", b64, len ); - g_free( b64 ); - } else { - unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, - len, &newLen ); - assert( NULL != bytes ); - - string_printf( query, fmt, "msg", connName, hid, devID, hid, connName, - "E", bytes, len ); - PQfreemem( bytes ); - } - - logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); - execSql( query ); } + + size_t newLen; + const char* fmt = "INSERT INTO " MSGS_TABLE " " + "(connname, hid, devid, token, %s, msglen) " + "VALUES( '%s', %d, %d, " + "(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), " + "%s'%s', %d)"; + + string query; + if ( m_useB64 ) { + gchar* b64 = g_base64_encode( buf, len ); + string_printf( query, fmt, "msg64", connName, hid, devID, hid, connName, + "", b64, len ); + g_free( b64 ); + } else { + unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, + len, &newLen ); + assert( NULL != bytes ); + + string_printf( query, fmt, "msg", connName, hid, devID, hid, connName, + "E", bytes, len ); + PQfreemem( bytes ); + } + + logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); + execSql( query ); } void From bc6922cb8b5b1e07cb2cb584c37a8a39630254b1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 06:33:06 -0700 Subject: [PATCH 052/126] add option to split tcp packets to test whether relay can handle reassembly. --- xwords4/linux/linuxmain.c | 15 ++++++++++++++- xwords4/linux/main.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 39f103e72..b2af84b6d 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -549,6 +549,7 @@ typedef enum { ,CMD_SKIPCONFIRM ,CMD_VERTICALSCORE ,CMD_NOPEEK + ,CMD_SPLITPACKETS #ifdef XWFEATURE_CROSSHAIRS ,CMD_NOCROSSHAIRS #endif @@ -649,6 +650,8 @@ static CmdInfoRec CmdInfoRecs[] = { ,{ 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_SPLITPACKETS, false, "split-packets", "send tcp packets in " + "sections to test relay reassembly" } #ifdef XWFEATURE_CROSSHAIRS ,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs", "don't show crosshairs on board" } @@ -839,7 +842,14 @@ linux_tcp_send( const XP_U8* buf, XP_U16 buflen, result = send( sock, &netLen, sizeof(netLen), 0 ); if ( result == sizeof(netLen) ) { - result = send( sock, buf, buflen, 0 ); + if ( globals->params->splitPackets ) { + int halflen = buflen / 2; + result = send( sock, buf, halflen, 0 ); + sleep( 1 ); + result += send( sock, buf + halflen, buflen - halflen, 0 ); + } else { + result = send( sock, buf, buflen, 0 ); + } } if ( result <= 0 ) { XP_STATUSF( "closing non-functional socket" ); @@ -1900,6 +1910,9 @@ main( int argc, char** argv ) case CMD_NOPEEK: mainParams.allowPeek = XP_FALSE; break; + case CMD_SPLITPACKETS: + mainParams.splitPackets = XP_TRUE; + break; #ifdef XWFEATURE_CROSSHAIRS case CMD_NOCROSSHAIRS: mainParams.hideCrosshairs = XP_TRUE; diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index 2ae3bf294..b68cd2685 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -90,6 +90,7 @@ typedef struct LaunchParams { XP_Bool skipGameOver; XP_Bool useMmap; XP_Bool closeStdin; + XP_Bool splitPackets; #ifdef XWFEATURE_SEARCHLIMIT XP_Bool allowHintRect; #endif From d4cf37d2ef72a9a3db2ddf98c7232d432ebf1662 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 07:05:09 -0700 Subject: [PATCH 053/126] fix misspelling --- xwords4/relay/cref.cpp | 2 +- xwords4/relay/cref.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index 2061fdd56..86de3fdd0 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -102,7 +102,7 @@ CookieRef::ReInit( const char* cookie, const char* connName, CookieID cid, } else { m_delayMicros = 0; } - RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heatbeat ); + RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heartbeat ); logf( XW_LOGINFO, "initing cref for cookie %s, connName %s", m_cookie.c_str(), m_connName.c_str() ); diff --git a/xwords4/relay/cref.h b/xwords4/relay/cref.h index 48d1d2b80..c12a5d752 100644 --- a/xwords4/relay/cref.h +++ b/xwords4/relay/cref.h @@ -104,7 +104,7 @@ class CookieRef { const char* Cookie() const { return m_cookie.c_str(); } const char* ConnName() { return m_connName.c_str(); } - int GetHeartbeat() { return m_heatbeat; } + int GetHeartbeat() { return m_heartbeat; } const AddrInfo* SocketForHost( HostID dest ); HostID HostForSocket( const AddrInfo* addr ); @@ -275,7 +275,7 @@ class CookieRef { pthread_rwlock_t m_socketsRWLock; vector m_sockets; - int m_heatbeat; /* might change per carrier or something. */ + int m_heartbeat; /* might change per carrier or something. */ string m_cookie; /* cookie used for initial connections */ string m_connName; /* globally unique name */ CookieID m_cid; /* Unique among current games on this server */ From 519f90a69a444ab4c7eed423a9c5b786cf47e000 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 07:09:57 -0700 Subject: [PATCH 054/126] go with non-blocking sockets for tcp connections, adding the ability to reassemble packets that arrive in separate recv() calls. --- xwords4/relay/udpqueue.cpp | 116 +++++++++++++++++++++++++++++-------- xwords4/relay/udpqueue.h | 34 +++++++++-- xwords4/relay/xwrelay.cpp | 24 +++++--- 3 files changed, 135 insertions(+), 39 deletions(-) diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index c4a89eac3..882f60897 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -37,9 +37,41 @@ UdpThreadClosure::logStats() } } +bool +PartialPacket::stillGood() const +{ + return 0 == m_errno + || EAGAIN == m_errno + || EWOULDBLOCK == m_errno; +} + +bool +PartialPacket::readAtMost( int len ) +{ + bool success = false; + uint8_t tmp[len]; + ssize_t nRead = recv( m_sock, tmp, len, 0 ); + if ( 0 > nRead ) { // error case + m_errno = errno; + logf( XW_LOGERROR, "%s(len=%d): recv failed: %d (%s)", __func__, len, + m_errno, strerror(m_errno) ); + } else if ( 0 == nRead ) { // remote socket closed + logf( XW_LOGINFO, "%s: remote closed", __func__ ); + m_errno = -1; // so stillGood will fail + } else { + m_errno = 0; + success = len == nRead; + int curSize = m_buf.size(); + m_buf.resize( nRead + curSize ); + memcpy( &m_buf[curSize], tmp, nRead ); + } + return success; +} + UdpQueue::UdpQueue() { m_nextID = 0; + pthread_mutex_init ( &m_partialsMutex, NULL ); pthread_mutex_init ( &m_queueMutex, NULL ); pthread_cond_init( &m_queueCondVar, NULL ); @@ -54,6 +86,7 @@ UdpQueue::~UdpQueue() { pthread_cond_destroy( &m_queueCondVar ); pthread_mutex_destroy ( &m_queueMutex ); + pthread_mutex_destroy ( &m_partialsMutex ); } UdpQueue* @@ -65,52 +98,85 @@ UdpQueue::get() return s_instance; } +// return false if socket should no longer be used bool UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) { - bool success = false; + PartialPacket* packet; + int sock = addr->socket(); - unsigned short msgLen; - ssize_t nRead = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL ); - if ( 0 == nRead ) { - logf( XW_LOGINFO, "%s: recv(sock=%d) => 0: remote closed", __func__, sock ); - } else if ( nRead != sizeof(msgLen) ) { - logf( XW_LOGERROR, "%s: first recv => %d: %s", __func__, - nRead, strerror(errno) ); + + // Hang onto this mutex for as long as we may be writing to the packet + // since having it deleted while in use would be bad. + MutexLock ml( &m_partialsMutex ); + + map::iterator iter = m_partialPackets.find( sock ); + if ( m_partialPackets.end() == iter ) { + packet = new PartialPacket( sock ); + m_partialPackets.insert( pair( sock, packet ) ); } else { - msgLen = ntohs( msgLen ); - if ( MAX_MSG_LEN <= msgLen ) { - logf( XW_LOGERROR, "%s: message of len %d too large; dropping", __func__, msgLen ); - } else { - unsigned char buf[msgLen]; - nRead = recv( sock, buf, msgLen, MSG_WAITALL ); - if ( nRead == msgLen ) { - logf( XW_LOGINFO, "%s: read %d bytes on socket %d", __func__, nRead, sock ); - handle( addr, buf, msgLen, cb ); - success = true; - } else { - logf( XW_LOGERROR, "%s: second recv failed: %s", __func__, - strerror(errno) ); - } + packet = iter->second; + } + + // First see if we've read the length bytes + if ( packet->readSoFar() < sizeof( packet->m_len ) ) { + if ( packet->readAtMost( sizeof(packet->m_len) - packet->readSoFar() ) ) { + packet->m_len = ntohs(*(unsigned short*)packet->data()); } } - return success; + + if ( packet->readSoFar() >= sizeof( packet->m_len ) ) { + assert( 0 < packet->m_len ); + int leftToRead = + packet->m_len - (packet->readSoFar() - sizeof(packet->m_len)); + if ( packet->readAtMost( leftToRead ) ) { + handle( addr, packet->data() + sizeof(packet->m_len), + packet->m_len, cb ); + packet = NULL; + newSocket_locked( sock ); + } + } + + return NULL == packet || packet->stillGood(); } void -UdpQueue::handle( const AddrInfo* addr, unsigned char* buf, int len, +UdpQueue::handle( const AddrInfo* addr, const uint8_t* buf, int len, QueueCallback cb ) { UdpThreadClosure* utc = new UdpThreadClosure( addr, buf, len, cb ); MutexLock ml( &m_queueMutex ); int id = ++m_nextID; utc->setID( id ); - logf( XW_LOGINFO, "%s: enqueuing packet %d", __func__, id ); + logf( XW_LOGINFO, "%s: enqueuing packet %d (len %d)", __func__, id, len ); m_queue.push_back( utc ); pthread_cond_signal( &m_queueCondVar ); } +void +UdpQueue::newSocket_locked( int sock ) +{ + map::iterator iter = m_partialPackets.find( sock ); + if ( m_partialPackets.end() != iter ) { + delete iter->second; + m_partialPackets.erase( iter ); + } +} +void +UdpQueue::newSocket( int sock ) +{ + MutexLock ml( &m_partialsMutex ); + newSocket_locked( sock ); +} + +void +UdpQueue::newSocket( const AddrInfo* addr ) +{ + assert( addr->isTCP() ); + newSocket( addr->socket() ); +} + void* UdpQueue::thread_main() { diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index 0aa04afa0..12307646a 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -36,9 +36,9 @@ typedef void (*QueueCallback)( UdpThreadClosure* closure ); class UdpThreadClosure { public: - UdpThreadClosure( const AddrInfo* addr, unsigned char* buf, + UdpThreadClosure( const AddrInfo* addr, const uint8_t* buf, int len, QueueCallback cb ) - : m_buf(new unsigned char[len]) + : m_buf(new uint8_t[len]) , m_len(len) , m_addr(*addr) , m_cb(cb) @@ -60,7 +60,7 @@ public: int getID( void ) { return m_id; } private: - unsigned char* m_buf; + uint8_t* m_buf; int m_len; AddrInfo m_addr; QueueCallback m_cb; @@ -69,25 +69,49 @@ public: int m_id; }; +class PartialPacket { + public: + PartialPacket(int sock) { + m_sock = sock; + m_len = 0; + m_errno = 0; + } + bool stillGood() const ; + bool readAtMost( int len ); + size_t readSoFar() const { return m_buf.size(); } + const uint8_t* data() const { return m_buf.data(); } + + unsigned short m_len; + private: + + vector m_buf; + int m_sock; + int m_errno; +}; + class UdpQueue { public: static UdpQueue* get(); UdpQueue(); ~UdpQueue(); bool handle( const AddrInfo* addr, QueueCallback cb ); - void handle( const AddrInfo* addr, unsigned char* buf, int len, + void handle( const AddrInfo* addr, const uint8_t* buf, int len, QueueCallback cb ); - void forgetSocket( const AddrInfo* addr ); + void newSocket( int socket ); + void newSocket( const AddrInfo* addr ); private: + void newSocket_locked( int sock ); static void* thread_main_static( void* closure ); void* thread_main(); + pthread_mutex_t m_partialsMutex; pthread_mutex_t m_queueMutex; pthread_cond_t m_queueCondVar; deque m_queue; // map > m_bySocket; int m_nextID; + map m_partialPackets; }; #endif diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 33bc9a2cb..b10f0ca90 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -453,8 +453,8 @@ send_with_length_unsafe( const AddrInfo* addr, const unsigned char* buf, if ( addr->isCurrent() ) { int socket = addr->socket(); unsigned short len = htons( bufLen ); - ssize_t nSent = send( socket, &len, 2, 0 ); - if ( nSent == 2 ) { + ssize_t nSent = send( socket, &len, sizeof(len), 0 ); + if ( nSent == sizeof(len) ) { nSent = send( socket, buf, bufLen, 0 ); if ( nSent == ssize_t(bufLen) ) { logf( XW_LOGINFO, "sent %d bytes on socket %d", nSent, socket ); @@ -1408,7 +1408,7 @@ udp_thread_proc( UdpThreadClosure* utc ) static void read_udp_packet( int udpsock ) { - unsigned char buf[MAX_MSG_LEN]; + uint8_t buf[MAX_MSG_LEN]; AddrInfo::AddrUnion saddr; memset( &saddr, 0, sizeof(saddr) ); socklen_t fromlen = sizeof(saddr.addr_in); @@ -1448,6 +1448,8 @@ string_printf( string& str, const char* fmt, ... ) } } +// Going with non-blocking instead +#if 0 static void set_timeouts( int sock ) { @@ -1472,6 +1474,7 @@ set_timeouts( int sock ) assert( 0 ); } } +#endif static void enable_keepalive( int sock ) @@ -1897,15 +1900,17 @@ main( int argc, char** argv ) errno, strerror(errno) ); assert( 0 ); // we're leaking files or load has grown } else { - // I've seen a bug where we accept but never service - // connections. Sockets are not closed, and so the - // number goes up. Probably need a watchdog instead, - // but this will work around it. + // I've seen a bug where we accept but never service + // connections. Sockets are not closed, and so the + // number goes up. Probably need a watchdog instead, + // but this will work around it. assert( g_maxsocks > newSock ); /* Set timeout so send and recv won't block forever */ - set_timeouts( newSock ); - + // set_timeouts( newSock ); + + int err = fcntl( newSock, F_SETFL, O_NONBLOCK ); + assert( 0 == err ); enable_keepalive( newSock ); logf( XW_LOGINFO, @@ -1918,6 +1923,7 @@ main( int argc, char** argv ) perGame ? game_thread_proc : proxy_thread_proc, &addr ); + UdpQueue::get()->newSocket( &addr ); } --retval; } From aca3a96b54dc923b11d7d57f8d865176c43e90ad Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 07:18:53 -0700 Subject: [PATCH 055/126] fix pattern so ended games are detected --- xwords4/linux/scripts/discon_ok2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 678c8d2b9..1bd691656 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -58,7 +58,7 @@ function connName() { LOG=$1 grep 'got_connect_cmd: connName' $LOG | \ tail -n 1 | \ - sed 's,^.*connName: \"\(.*\)\"$,\1,' + sed 's,^.*connName: \"\(.*\)\" (reconnect=.)$,\1,' } function check_room() { From 58426544189b57883078355d0470ff51d2c55f70 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 07:30:38 -0700 Subject: [PATCH 056/126] fix pattern --- xwords4/linux/scripts/relayID.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/linux/scripts/relayID.sh b/xwords4/linux/scripts/relayID.sh index f5109cc50..495e393c8 100755 --- a/xwords4/linux/scripts/relayID.sh +++ b/xwords4/linux/scripts/relayID.sh @@ -24,7 +24,7 @@ while [ $# -ge 1 ]; do while read LINE; do case "$LINE" in *got_connect_cmd:\ connName* ) - CONNNAME="$(echo $LINE | sed 's,^.*connName: "\(.*\)"$,\1,')" + CONNNAME="$(echo $LINE | sed 's,^.*connName: "\(.*\)" .*$,\1,')" ;; *got_connect_cmd:\ set\ hostid* ) HOSTID=$(echo $LINE | sed 's,^.*set hostid: \(.\)$,\1,') From 5612bb000b0a9bdc301f716c34d41fcbcb5969b2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 24 Jun 2013 18:37:49 -0700 Subject: [PATCH 057/126] quick hack to allow testing intermix of chat with normal moves --- xwords4/linux/cursesmain.c | 46 ++++++++++++++++++++++++++++++++++++++ xwords4/linux/linuxmain.c | 5 +++++ xwords4/linux/main.h | 1 + 3 files changed, 52 insertions(+) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 370b053ea..2e72e3b44 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include /* gethostbyname */ #include @@ -1513,6 +1515,15 @@ curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo ) } /* curses_util_makeStreamFromAddr */ #endif +#ifdef XWFEATURE_CHAT +static void +curses_util_showChat( XW_UtilCtxt* XP_UNUSED(uc), const XP_UCHAR* const msg ) +{ + XP_LOGF( "%s: got \"%s\"", __func__, msg ); +} +#endif + + static void setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util ) { @@ -1526,6 +1537,10 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util ) #ifndef XWFEATURE_STANDALONE_ONLY util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr; #endif +#ifdef XWFEATURE_CHAT + util->vtable->m_util_showChat = curses_util_showChat; +#endif + util->vtable->m_util_userQuery = curses_util_userQuery; util->vtable->m_util_confirmTrade = curses_util_confirmTrade; util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank; @@ -1699,6 +1714,32 @@ handle_stdin( GIOChannel* XP_UNUSED_DBG(source), GIOCondition condition, } #endif +static gboolean +chatsTimerFired( gpointer data ) +{ + CursesAppGlobals* globals = (CursesAppGlobals*)data; + + GameStateInfo gsi; + game_getState( &globals->cGlobals.game, &gsi ); + + if ( gsi.gameIsConnected ) { + XP_UCHAR msg[128]; + struct tm* timp; + struct timeval tv; + struct timezone tz; + + gettimeofday( &tv, &tz ); + timp = localtime( &tv.tv_sec ); + + snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d:", + timp->tm_hour, timp->tm_min, timp->tm_sec ); + server_sendChat( globals->cGlobals.game.server, msg ); + XP_LOGF( "%s: sent \"%s\"", __func__, msg ); + } + + return TRUE; +} + void cursesmain( XP_Bool isServer, LaunchParams* params ) { @@ -1788,6 +1829,11 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) } else if ( !!params->nbs && !!params->fileName ) { do_nbs_then_close( &g_globals.cGlobals, &procs ); } else { + if ( 0 != params->chatsInterval ) { + (void)g_timeout_add_seconds( params->chatsInterval, chatsTimerFired, + &g_globals ); + } + XP_Bool opened = XP_FALSE; initCurses( &g_globals ); getmaxyx( g_globals.boardWin, height, width ); diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index a001d7daf..4c6d4ed40 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -549,6 +549,7 @@ typedef enum { ,CMD_SKIPCONFIRM ,CMD_VERTICALSCORE ,CMD_NOPEEK + ,CMD_CHAT #ifdef XWFEATURE_CROSSHAIRS ,CMD_NOCROSSHAIRS #endif @@ -649,6 +650,7 @@ static CmdInfoRec CmdInfoRecs[] = { ,{ 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_CHAT, true, "send-chat", "send a chat every seconds" } #ifdef XWFEATURE_CROSSHAIRS ,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs", "don't show crosshairs on board" } @@ -1898,6 +1900,9 @@ main( int argc, char** argv ) case CMD_NOPEEK: mainParams.allowPeek = XP_FALSE; break; + case CMD_CHAT: + mainParams.chatsInterval = atoi(optarg); + break; #ifdef XWFEATURE_CROSSHAIRS case CMD_NOCROSSHAIRS: mainParams.hideCrosshairs = XP_TRUE; diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index 2ae3bf294..3fe4f1d97 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -90,6 +90,7 @@ typedef struct LaunchParams { XP_Bool skipGameOver; XP_Bool useMmap; XP_Bool closeStdin; + XP_U16 chatsInterval; /* 0 means disabled */ #ifdef XWFEATURE_SEARCHLIMIT XP_Bool allowHintRect; #endif From 27e9b48b785eebe6045b47a4d042798cbe2af1a4 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 07:58:15 -0700 Subject: [PATCH 058/126] fix timestamp math that had newer sometimes less than older --- xwords4/relay/addrinfo.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xwords4/relay/addrinfo.cpp b/xwords4/relay/addrinfo.cpp index 504b94ebf..b3a2fd213 100644 --- a/xwords4/relay/addrinfo.cpp +++ b/xwords4/relay/addrinfo.cpp @@ -27,6 +27,8 @@ #include "xwrelay_priv.h" #include "tpool.h" +// static uint32_t s_prevCreated = 0L; + void AddrInfo::construct( int socket, const AddrUnion* saddr, bool isTCP ) { @@ -34,8 +36,12 @@ AddrInfo::construct( int socket, const AddrUnion* saddr, bool isTCP ) struct timespec tp; clock_gettime( CLOCK_MONOTONIC, &tp ); - m_created = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000); - logf( XW_LOGINFO, "%s: m_created for socket %d: 0x%lx", __func__, socket, m_created ); + /* convert to milliseconds */ + m_created = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000); + logf( XW_LOGINFO, "%s: m_created for socket %d: %lx", + __func__, socket, m_created ); + /* assert( m_created >= s_prevCreated ); */ + /* s_prevCreated = m_created; */ m_socket = socket; m_isTCP = isTCP; From aa7e9bf751721c83f7aad547061f6cec9bb6bb3e Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 07:59:40 -0700 Subject: [PATCH 059/126] fix compile when debug define turned on --- xwords4/relay/cidlock.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/xwords4/relay/cidlock.cpp b/xwords4/relay/cidlock.cpp index bb9fa7f98..2f42ce6c1 100644 --- a/xwords4/relay/cidlock.cpp +++ b/xwords4/relay/cidlock.cpp @@ -72,7 +72,7 @@ CidLock::print_claimed( const char* caller ) string str; string_printf( str, "after %s: ", caller ); // Assume we have the mutex!!!! - map< CookieID, CidInfo*>::iterator iter; + map< CookieID, CidInfo*>::const_iterator iter; for ( iter = m_infos.begin(); iter != m_infos.end(); ++iter ) { CidInfo* info = iter->second; if ( 0 == info->GetOwner() ) { @@ -81,7 +81,7 @@ CidLock::print_claimed( const char* caller ) string_printf( str, "%d,", info->GetCid() ); } } - string_printf( str, "%d,", " (plus %d unclaimed.)", unclaimed ); + string_printf( str, " (plus %d unclaimed.)", unclaimed ); logf( XW_LOGINFO, "%s: claimed: %s", __func__, str.c_str() ); } #else @@ -137,13 +137,14 @@ CidLock::ClaimSocket( const AddrInfo* addr ) { CidInfo* info = NULL; #ifdef CIDLOCK_DEBUG - logf( XW_LOGINFO, "%s(sock=%d)", __func__, sock ); + logf( XW_LOGINFO, "%s(sock=%d)", __func__, addr->socket() ); #endif for ( ; ; ) { MutexLock ml( &m_infos_mutex ); - map::iterator iter; - for ( iter = m_infos.begin(); NULL == info && iter != m_infos.end(); ++iter ) { + map::const_iterator iter; + for ( iter = m_infos.begin(); NULL == info && iter != m_infos.end(); + ++iter ) { const vector& addrs = iter->second->GetAddrs(); vector::const_iterator iter2; for ( iter2 = addrs.begin(); iter2 != addrs.end(); ++iter2 ) { @@ -164,7 +165,7 @@ CidLock::ClaimSocket( const AddrInfo* addr ) break; } #ifdef CIDLOCK_DEBUG - logf( XW_LOGINFO, "%s(sock=%d): waiting....", __func__, sock ); + logf( XW_LOGINFO, "%s(sock=%d): waiting....", __func__, addr->socket() ); #endif pthread_cond_wait( &m_infos_condvar, &m_infos_mutex ); } From 8cb89976cb5ee731c122545312243a00243d7bb8 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 20:03:03 -0700 Subject: [PATCH 060/126] new quickie release to fix Play Store filtering out devices without phones -- or so I think. Make SMS optional, and up version strings. --- xwords4/android/XWords4/AndroidManifest.xml | 24 +++++++++++++++++-- xwords4/android/XWords4/project.properties | 2 +- xwords4/android/XWords4/res/raw/changes | 13 +++------- .../android/XWords4/res/values/app_name.xml | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index a11eab419..4e8c9b55e 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,11 +22,18 @@ to come from a domain that you own or have control over. --> - + + + @@ -38,6 +45,19 @@ + + + + + diff --git a/xwords4/android/XWords4/project.properties b/xwords4/android/XWords4/project.properties index c1fd41ab1..ddd0fc418 100644 --- a/xwords4/android/XWords4/project.properties +++ b/xwords4/android/XWords4/project.properties @@ -10,4 +10,4 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=android-8 +target=android-10 diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index 80f99854d..ee10ed83e 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -5,21 +5,14 @@ -Crosswords 4.4 beta 60 release +Crosswords 4.4 beta 61 release

New with this release

    -
  • Allow alternate spellings for tiles in the Find field in the - wordlist browser, e.g. 'a' for 'A' and 'L-L' for 'L·L' (in - Catalan). The new wordlist format requires this upgrade, so I will - wait a few weeks before releasing new wordlists.
  • -
  • Upgrade built-in English wordlists.
  • +
  • Change manifest to make SMS optional, allowing installation on + tablets and other devices without phones.
  • -
  • Don't run SMSService if play via SSM is disabled
  • - -
  • Fix bug with invites to SMS games where invitee is missing - wordlist

Next up

diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 324c6a8e8..332550f5b 100644 --- a/xwords4/android/XWords4/res/values/app_name.xml +++ b/xwords4/android/XWords4/res/values/app_name.xml @@ -1,5 +1,5 @@ - 4.4 beta 60 + 4.4 beta 61 From 649969bc304005f98e07b81fc3e164d84f8933ca Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 22:52:13 -0700 Subject: [PATCH 061/126] send ALL_HERE to all players --- xwords4/relay/cref.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index 86de3fdd0..c5fd9805e 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -1292,7 +1292,7 @@ CookieRef::sendAllHere( bool initial ) message for it. Would be better if could look up rather than run through the vector each time. */ HostID dest; - for ( dest = 1; dest <= m_nPlayersHere; ++dest ) { + for ( dest = 1; dest <= m_nPlayersSought; ++dest ) { bool sent = false; *idLoc = dest; /* write in this target's hostId */ From a1d272bf4f2e8fc1479a2e0f5868852f96167183 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 22:54:13 -0700 Subject: [PATCH 062/126] log sockets associated with packets --- xwords4/relay/udpqueue.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index 882f60897..d06d29e42 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -53,10 +53,10 @@ PartialPacket::readAtMost( int len ) ssize_t nRead = recv( m_sock, tmp, len, 0 ); if ( 0 > nRead ) { // error case m_errno = errno; - logf( XW_LOGERROR, "%s(len=%d): recv failed: %d (%s)", __func__, len, - m_errno, strerror(m_errno) ); + logf( XW_LOGERROR, "%s(len=%d, socket=%d): recv failed: %d (%s)", __func__, + len, m_sock, m_errno, strerror(m_errno) ); } else if ( 0 == nRead ) { // remote socket closed - logf( XW_LOGINFO, "%s: remote closed", __func__ ); + logf( XW_LOGINFO, "%s: remote closed (socket=%d)", __func__, m_sock ); m_errno = -1; // so stillGood will fail } else { m_errno = 0; @@ -148,7 +148,8 @@ UdpQueue::handle( const AddrInfo* addr, const uint8_t* buf, int len, MutexLock ml( &m_queueMutex ); int id = ++m_nextID; utc->setID( id ); - logf( XW_LOGINFO, "%s: enqueuing packet %d (len %d)", __func__, id, len ); + logf( XW_LOGINFO, "%s: enqueuing packet %d (socket %d, len %d)", + __func__, id, addr->socket(), len ); m_queue.push_back( utc ); pthread_cond_signal( &m_queueCondVar ); @@ -191,7 +192,8 @@ UdpQueue::thread_main() pthread_mutex_unlock( &m_queueMutex ); utc->noteDequeued(); - logf( XW_LOGINFO, "%s: dispatching packet %d", __func__, utc->getID() ); + logf( XW_LOGINFO, "%s: dispatching packet %d (socket %d)", __func__, + utc->getID(), utc->addr()->socket() ); (*utc->cb())( utc ); utc->logStats(); delete utc; From d801b23a871c867cf3366cea66d2cae7f9a5a4ac Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Jun 2013 23:40:46 -0700 Subject: [PATCH 063/126] add missing config option --- xwords4/relay/xwrelay.conf_tmplate | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xwords4/relay/xwrelay.conf_tmplate b/xwords4/relay/xwrelay.conf_tmplate index 66d455a6b..2773342e0 100644 --- a/xwords4/relay/xwrelay.conf_tmplate +++ b/xwords4/relay/xwrelay.conf_tmplate @@ -31,6 +31,8 @@ UDPPORT=10997 # default 5 SOCK_TIMEOUT_SECONDS=5 +# How many tcp sockets at once (to prevent leaks). default: 100 +MAXSOCKS=256 # And the control port is? CTLPORT=11000 From d7f8e8e1988570bf676085a955d0865cb0ce6d55 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:27:55 -0700 Subject: [PATCH 064/126] rq is hanging, so set timeouts on its blocking sockets --- xwords4/relay/rq.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/xwords4/relay/rq.c b/xwords4/relay/rq.c index 617e9992a..6f7ddf8c0 100644 --- a/xwords4/relay/rq.c +++ b/xwords4/relay/rq.c @@ -234,6 +234,22 @@ connect_socket( void ) to_sock.sin_family = AF_INET; to_sock.sin_port = htons( g_port ); + struct timeval tv; + tv.tv_sec = 5; /* seconds */ + tv.tv_usec = 0; /* microseconds */ + + int result = setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) ); + if ( 0 != result ) { + fprintf( stderr, "setsockopt=>%d (%s)", errno, strerror(errno) ); + assert( 0 ); + } + result = setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) ); + if ( 0 != result ) { + fprintf( stderr, "setsockopt=>%d (%s)", errno, strerror(errno) ); + assert( 0 ); + } + + struct hostent* hostip; hostip = gethostbyname( g_host ); memcpy( &(to_sock.sin_addr.s_addr), hostip->h_addr_list[0], From 909640c82f236eeeb9a4c40605acd0d457454111 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:36:02 -0700 Subject: [PATCH 065/126] cleanup/improve logging --- xwords4/relay/cidlock.cpp | 10 ++++++---- xwords4/relay/tpool.cpp | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/cidlock.cpp b/xwords4/relay/cidlock.cpp index 2f42ce6c1..bfc10555b 100644 --- a/xwords4/relay/cidlock.cpp +++ b/xwords4/relay/cidlock.cpp @@ -89,10 +89,11 @@ CidLock::print_claimed( const char* caller ) #endif CidInfo* -CidLock::Claim( CookieID cid ) +CidLock::Claim( const CookieID origCid ) { + CookieID cid = origCid; #ifdef CIDLOCK_DEBUG - logf( XW_LOGINFO, "%s(%d)", __func__, cid ); + logf( XW_LOGINFO, "%s(%d)", __func__, origCid ); #endif CidInfo* info = NULL; pthread_t self = pthread_self(); @@ -127,7 +128,7 @@ CidLock::Claim( CookieID cid ) pthread_cond_wait( &m_infos_condvar, &m_infos_mutex ); } #ifdef CIDLOCK_DEBUG - logf( XW_LOGINFO, "%s(%d): DONE", __func__, cid ); + logf( XW_LOGINFO, "%s(%d): DONE", __func__, origCid ); #endif return info; } /* CidLock::Claim */ @@ -191,7 +192,8 @@ CidLock::Relinquish( CidInfo* claim, bool drop ) assert( claim->GetOwner() == pthread_self() ); if ( drop ) { #ifdef CIDLOCK_DEBUG - logf( XW_LOGINFO, "%s: deleting %p", __func__, iter->second ); + logf( XW_LOGINFO, "%s: deleting %p (cid=%d)", + __func__, claim, claim->GetCid() ); #endif m_infos.erase( iter ); claim->SetOwner( 0 ); diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index 7bb5eaf15..8cd246488 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -225,7 +225,8 @@ XWThreadPool::IsCurrent( const AddrInfo* addr ) assert( !sockFound ); sockFound = true; result = iter->second.m_addr.created() <= addr->created(); - logf( XW_LOGINFO, "%s(sock=%d)=>%d (%lx vs %lx)", __func__, sock, result, + logf( XW_LOGINFO, "%s(sock=%d)=>%d (%lx vs %lx)", + __func__, sock, result, iter->second.m_addr.created(), addr->created() ); } } From 9138cd15b7e9ee232ae5a48d9bd1be781662f18e Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:38:34 -0700 Subject: [PATCH 066/126] fix problems with devices reconnecting when their ACKs didn't arrive and their slots are empty or have been reassigned: basically we check if a device goes where it expects, and if not treat it as a new connection rather than a reconnect, meaning its hostid may change. Existing device code seems ok with that -- and at any rate I don't think ACKs get dropped much in the wild. --- xwords4/relay/crefmgr.cpp | 68 ++++++++++++++++++++++----------------- xwords4/relay/crefmgr.h | 8 ++--- xwords4/relay/dbmgr.cpp | 51 +++++++++++++++++++++++++++-- xwords4/relay/dbmgr.h | 5 +++ xwords4/relay/xwrelay.cpp | 6 ++-- 5 files changed, 99 insertions(+), 39 deletions(-) diff --git a/xwords4/relay/crefmgr.cpp b/xwords4/relay/crefmgr.cpp index 36cad1f94..5b36b68c5 100644 --- a/xwords4/relay/crefmgr.cpp +++ b/xwords4/relay/crefmgr.cpp @@ -216,9 +216,8 @@ CRefMgr::getFromFreeList( void ) /* connect case */ CidInfo* -CRefMgr::getMakeCookieRef( const char* cookie, HostID hid, - int nPlayersH, int nPlayersT, int langCode, - int seed, bool wantsPublic, +CRefMgr::getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersT, + int langCode, int seed, bool wantsPublic, bool makePublic, bool* seenSeed ) { CidInfo* cinfo; @@ -291,20 +290,22 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie, int langCode, bool isPublic, bool* isDead ) { CookieRef* cref = NULL; - CidInfo* cinfo; + CidInfo* cinfo = NULL; for ( ; ; ) { /* for: see comment above */ /* fetch these from DB */ char curCookie[MAX_INVITE_LEN+1]; int curLangCode; - int nPlayersT = 0; - int nAlreadyHere = 0; + int nAlreadyHere = nPlayersH; + + CookieID cid; + if ( !m_db->FindGameFor( connName, curCookie, sizeof(curCookie), + seed, hid, nPlayersH, nPlayersS, + &curLangCode, isDead, &cid ) ) { + break; + } - CookieID cid = m_db->FindGame( connName, curCookie, sizeof(curCookie), - &curLangCode, &nPlayersT, &nAlreadyHere, - isDead ); if ( 0 != cid ) { /* already open */ - assert( nPlayersT == nPlayersS ); cinfo = m_cidlock->Claim( cid ); if ( NULL == cinfo->GetRef() ) { m_cidlock->Relinquish( cinfo, true ); @@ -316,26 +317,19 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie, cinfo = m_cidlock->Claim(); cid = cinfo->GetCid(); - if ( nPlayersT == 0 ) { /* wasn't in the DB */ - m_db->AddNew( cookie, connName, cid, langCode, nPlayersS, isPublic ); - curLangCode = langCode; - nPlayersT = nPlayersS; - } else { - if ( !m_db->AddCID( connName, cid ) ) { - m_cidlock->Relinquish( cinfo, true ); - continue; - } - cookie = curCookie; + if ( !m_db->AddCID( connName, cid ) ) { + m_cidlock->Relinquish( cinfo, true ); + continue; } + cookie = curCookie; - assert( nPlayersT == nPlayersS ); - cref = AddNew( cookie, connName, cid, curLangCode, nPlayersT, + cref = AddNew( cookie, connName, cid, curLangCode, nPlayersS, nAlreadyHere ); cinfo->SetRef( cref ); } break; } /* for */ - assert( cinfo->GetRef() ); + assert( NULL == cinfo || cinfo->GetRef() ); return cinfo; } /* getMakeCookieRef */ @@ -612,8 +606,8 @@ SafeCref::SafeCref( const char* cookie, const AddrInfo* addr, int clientVers, { CidInfo* cinfo; - cinfo = m_mgr->getMakeCookieRef( cookie, 0, nPlayersH, nPlayersS, langCode, - gameSeed, wantsPublic, makePublic, + cinfo = m_mgr->getMakeCookieRef( cookie, nPlayersH, nPlayersS, + langCode, gameSeed, wantsPublic, makePublic, &m_seenSeed ); if ( cinfo != NULL ) { CookieRef* cref = cinfo->GetRef(); @@ -623,16 +617,23 @@ SafeCref::SafeCref( const char* cookie, const AddrInfo* addr, int clientVers, } } -/* REconnect case */ +/* Reconnect case + * + * Device thinks it's connected, but we may disagree, e.g. if it sent an ACK + * we didn't receive in time. So we may actually wind up creating a new row, + * with a new connname, in the games DB in response to this! + * + */ SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid, - const AddrInfo* addr, int clientVers, DevID* devID, int nPlayersH, - int nPlayersS, unsigned short gameSeed, int langCode, - bool wantsPublic, bool makePublic ) + const AddrInfo* addr, int clientVers, DevID* devID, + int nPlayersH, int nPlayersS, unsigned short gameSeed, + int langCode, bool wantsPublic, bool makePublic ) : m_cinfo( NULL ) , m_mgr( CRefMgr::Get() ) , m_addr( *addr ) , m_clientVersion( clientVers ) , m_devID( devID ) + , m_hid( hid ) , m_isValid( false ) { CidInfo* cinfo; @@ -642,6 +643,15 @@ SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid, cinfo = m_mgr->getMakeCookieRef( connName, cookie, hid, nPlayersH, nPlayersS, gameSeed, langCode, wantsPublic || makePublic, &isDead ); + + /* If the reconnect doesn't check out, treat it as a connect */ + if ( NULL == cinfo ) { + logf( XW_LOGINFO, "%s: taking a second crack", __func__ ); + m_hid = HOST_ID_NONE; + cinfo = m_mgr->getMakeCookieRef( cookie, nPlayersH, nPlayersS, + langCode, gameSeed, + wantsPublic, makePublic, &m_seenSeed ); + } if ( cinfo != NULL ) { assert( cinfo->GetCid() == cinfo->GetRef()->GetCid() ); m_locked = cinfo->GetRef()->Lock(); diff --git a/xwords4/relay/crefmgr.h b/xwords4/relay/crefmgr.h index 242f7fbf3..d7770632f 100644 --- a/xwords4/relay/crefmgr.h +++ b/xwords4/relay/crefmgr.h @@ -117,7 +117,7 @@ class CRefMgr { CookieRef* getFromFreeList( void ); /* connect case */ - CidInfo* getMakeCookieRef( const char* cookie, HostID hid, int nPlayersH, + CidInfo* getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersS, int langCode, int seed, bool wantsPublic, bool makePublic, bool* seenSeed ); @@ -218,8 +218,7 @@ class SafeCref { return false; } } - bool Reconnect( HostID srcID, int nPlayersH, int nPlayersS, - int seed, XWREASON* errp ) { + bool Reconnect( int nPlayersH, int nPlayersS, int seed, XWREASON* errp ) { bool success = false; *errp = XWRELAY_ERROR_NONE; if ( IsValid() ) { @@ -229,7 +228,7 @@ class SafeCref { *errp = XWRELAY_ERROR_DEADGAME; } else { success = cref->_Reconnect( m_clientVersion, m_devID, - srcID, nPlayersH, nPlayersS, seed, + m_hid, nPlayersH, nPlayersS, seed, &m_addr, m_dead ); } } @@ -392,6 +391,7 @@ class SafeCref { AddrInfo m_addr; int m_clientVersion; DevID* m_devID; + HostID m_hid; bool m_isValid; bool m_locked; bool m_dead; diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index f87062914..ff0b61e24 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -123,6 +123,39 @@ DBMgr::AddNew( const char* cookie, const char* connName, CookieID cid, PQclear( result ); } +/* Grab the row for a connname. If the params don't check out, return false. + */ +bool +DBMgr::FindGameFor( const char* connName, char* cookieBuf, int bufLen, + unsigned short seed, HostID hid, + int nPlayersH, int nPlayersS, + int* langP, bool* isDead, CookieID* cidp ) +{ + bool found = false; + + const char* fmt = "SELECT cid, room, lang, nPerDevice, dead FROM " + GAMES_TABLE " WHERE connName = '%s' AND nTotal = %d " + "AND %d = seeds[%d] AND 'A' = ack[%d] " + ; + string query; + string_printf( query, fmt, connName, nPlayersS, seed, hid, hid ); + logf( XW_LOGINFO, "query: %s", query.c_str() ); + + PGresult* result = PQexec( getThreadConn(), query.c_str() ); + assert( 1 >= PQntuples( result ) ); + found = 1 == PQntuples( result ); + if ( found ) { + *cidp = atoi( PQgetvalue( result, 0, 0 ) ); + snprintf( cookieBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) ); + *langP = atoi( PQgetvalue( result, 0, 2 ) ); + *isDead = 't' == PQgetvalue( result, 0, 4 )[0]; + } + PQclear( result ); + + logf( XW_LOGINFO, "%s(%s)=>%d", __func__, connName, found ); + return found; +} /* FindGameFor */ + CookieID DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen, int* langP, int* nPlayersTP, int* nPlayersHP, bool* isDead ) @@ -385,13 +418,25 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion, HostID newID = curID; if ( newID == HOST_ID_NONE ) { - int arr[4] = {0}; - readArray( connName, "nPerDevice", arr ); + int ackArr[4] = {0}; + int seedArr[4] = {0}; + readArray( connName, "nPerDevice", ackArr ); + readArray( connName, "seeds", seedArr ); + + // If our seed's already there, grab that slot. Otherwise grab the + // first empty one. + HostID firstEmpty = HOST_ID_NONE; for ( newID = HOST_ID_SERVER; newID <= 4; ++newID ) { - if ( arr[newID-1] == 0 ) { + if ( seedArr[newID-1] == seed ) { break; + } else if ( HOST_ID_NONE == firstEmpty && 0 == ackArr[newID-1] ) { + firstEmpty = newID; } } + + if ( 4 < newID && HOST_ID_NONE != firstEmpty ) { + newID = firstEmpty; + } logf( XW_LOGINFO, "%s: set newID = %d", __func__, newID ); } assert( newID <= 4 ); diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 8e12fc65f..70cf50ff5 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -54,6 +54,11 @@ class DBMgr { int* langP, int* nPlayersTP, int* nPlayersHP, bool* isDead ); + bool FindGameFor( const char* connName, char* cookieBuf, int bufLen, + unsigned short seed, HostID hid, + int nPlayersH, int nPlayersS, + int* langP, bool* isDead, CookieID* cidp ); + bool SeenSeed( const char* cookie, unsigned short seed, int langCode, int nPlayersT, bool wantsPublic, char* connNameBuf, int bufLen, int* nPlayersHP, diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index b10f0ca90..05c7a8ece 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -450,8 +450,8 @@ send_with_length_unsafe( const AddrInfo* addr, const unsigned char* buf, bool ok = false; if ( addr->isTCP() ) { + int socket = addr->socket(); if ( addr->isCurrent() ) { - int socket = addr->socket(); unsigned short len = htons( bufLen ); ssize_t nSent = send( socket, &len, sizeof(len), 0 ); if ( nSent == sizeof(len) ) { @@ -609,7 +609,7 @@ processReconnect( const unsigned char* bufp, int bufLen, const AddrInfo* addr ) cookie, srcID, addr, clientVersion, &devID, nPlayersH, nPlayersT, gameSeed, langCode, wantsPublic, makePublic ); - success = scr.Reconnect( srcID, nPlayersH, nPlayersT, gameSeed, + success = scr.Reconnect( nPlayersH, nPlayersT, gameSeed, &err ); // if ( !success ) { // assert( err != XWRELAY_ERROR_NONE ); @@ -712,7 +712,7 @@ forwardMessage( const unsigned char* buf, int buflen, const AddrInfo* addr ) success = scr.Forward( src, addr, dest, buf, buflen ); } else { /* won't work if not allcon; will be 0 */ - SafeCref scr( cookieID /*, true*/ ); + SafeCref scr( cookieID, true ); success = scr.Forward( src, addr, dest, buf, buflen ); } } From 8324b9c77092c109330b2ad4e2714c5a39133a94 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:39:58 -0700 Subject: [PATCH 067/126] fix compile error by making HELP another command. --- xwords4/linux/linuxmain.c | 55 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 035781b09..ad92cf36a 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -499,7 +499,8 @@ setOneSecondTimer( CommonGlobals* cGlobals ) #endif typedef enum { - CMD_SKIP_GAMEOVER + CMD_HELP + ,CMD_SKIP_GAMEOVER ,CMD_SHOW_OTHERSCORES ,CMD_HOSTIP ,CMD_DICT @@ -598,7 +599,8 @@ typedef struct _CmdInfoRec { } CmdInfoRec; static CmdInfoRec CmdInfoRecs[] = { - { CMD_SKIP_GAMEOVER, false, "skip-final", "skip final scores display" } + { CMD_HELP, false, "help", "print this message" } + ,{ CMD_SKIP_GAMEOVER, false, "skip-final", "skip final scores display" } ,{ CMD_SHOW_OTHERSCORES, false, "show-other", "show robot/remote scores" } ,{ CMD_HOSTIP, true, "hostip", "remote host ip address (for direct connect)" } ,{ CMD_DICT, true, "game-dict", "dictionary name for game" } @@ -1004,36 +1006,43 @@ linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize ) unsigned short tmp; ssize_t nRead = blocking_read( sock, (unsigned char*)&tmp, sizeof(tmp) ); if ( nRead != 2 ) { - linux_close_socket( cGlobals ); - comms_transportFailed( cGlobals->game.comms ); nRead = -1; } else { unsigned short packetSize = ntohs( tmp ); - assert( packetSize <= bufSize ); - nRead = blocking_read( sock, buf, packetSize ); - if ( nRead != packetSize ) { + if ( 0 == packetSize || bufSize <= packetSize ) { nRead = -1; } else { - LaunchParams* params = cGlobals->params; - ++params->nPacketsRcvd; - if ( params->dropNthRcvd == 0 ) { - /* do nothing */ - } else if ( params->dropNthRcvd > 0 ) { - if ( params->nPacketsRcvd == params->dropNthRcvd ) { - XP_LOGF( "%s: dropping %dth packet per --drop-nth-packet", - __func__, params->nPacketsRcvd ); - nRead = -1; - } + nRead = blocking_read( sock, buf, packetSize ); + if ( nRead != packetSize ) { + nRead = -1; } else { - if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) { - XP_LOGF( "%s: RANDOMLY dropping %dth packet " - "per --drop-nth-packet", - __func__, params->nPacketsRcvd ); - nRead = -1; + LaunchParams* params = cGlobals->params; + ++params->nPacketsRcvd; + if ( params->dropNthRcvd == 0 ) { + /* do nothing */ + } else if ( params->dropNthRcvd > 0 ) { + if ( params->nPacketsRcvd == params->dropNthRcvd ) { + XP_LOGF( "%s: dropping %dth packet per --drop-nth-packet", + __func__, params->nPacketsRcvd ); + nRead = -1; + } + } else { + if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) { + XP_LOGF( "%s: RANDOMLY dropping %dth packet " + "per --drop-nth-packet", + __func__, params->nPacketsRcvd ); + nRead = -1; + } } } } } + + if ( -1 == nRead ) { + linux_close_socket( cGlobals ); + comms_transportFailed( cGlobals->game.comms ); + } + XP_LOGF( "%s=>%d", __func__, nRead ); return nRead; } /* linux_relay_receive */ @@ -1675,7 +1684,7 @@ main( int argc, char** argv ) short index; opt = getopt_long_only( argc, argv, "", longopts, NULL ); switch ( opt ) { - case '?': + case CMD_HELP: usage(argv[0], NULL); break; case CMD_SKIP_GAMEOVER: From 0bb4f5b0f5f3e9c534452a0998c00eefb4e509b9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:41:16 -0700 Subject: [PATCH 068/126] fix occasional memory leak --- xwords4/linux/gtkmain.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index f5d4b9809..f2b18537f 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -2190,6 +2190,9 @@ gtk_socket_changed( void* closure, int oldSock, int newSock, void** storage ) globals ); info->channel = channel; info->watch = result; + if ( !!*storage ) { + XP_FREE( globals->cGlobals.params->util->mpool, *storage ); + } *storage = info; XP_LOGF( "g_io_add_watch(%d) => %d", newSock, result ); } @@ -2273,12 +2276,11 @@ drop_msg_toggle( GtkWidget* toggle, GtkAppGlobals* globals ) } /* drop_msg_toggle */ #endif -static GtkAppGlobals* g_globals_for_signal; static void handle_sigintterm( int XP_UNUSED(sig) ) { LOG_FUNC(); - gtk_main_quit(); + gtk_main_quit(); /* ok to call from signal handler? I bet not. */ } int @@ -2296,7 +2298,6 @@ gtkmain( LaunchParams* params, int argc, char *argv[] ) GtkWidget* dropCheck; #endif - g_globals_for_signal = &globals; struct sigaction act = { .sa_handler = handle_sigintterm }; sigaction( SIGINT, &act, NULL ); sigaction( SIGTERM, &act, NULL ); From b31858ee5ea2efddf49e6739bd37aa9033172423 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Jun 2013 00:42:55 -0700 Subject: [PATCH 069/126] log rather than assert when hostid changes -- though I'm not seeing this now that the relay doesn't send packets to the wrong device --- xwords4/common/comms.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 7ea6a715c..b0578dbda 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1437,8 +1437,11 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) case XWRELAY_ALLHERE: srcID = (XWHostID)stream_getU8( stream ); - XP_ASSERT( comms->r.myHostID == HOST_ID_NONE - || comms->r.myHostID == srcID ); + if ( comms->r.myHostID != HOST_ID_NONE + && comms->r.myHostID != srcID ) { + XP_LOGF( "%s: changing hostid from %d to %d", __func__, + comms->r.myHostID, srcID ); + } if ( 0 == comms->r.cookieID ) { XP_LOGF( "%s: cookieID still 0; background send?", From f9cebfe4441054151381e44eea5531162647ca1c Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 27 Jun 2013 06:23:15 -0700 Subject: [PATCH 070/126] track bytes sent per device rather than per game --- xwords4/relay/dbmgr.cpp | 4 ++-- xwords4/relay/xwrelay.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index ff0b61e24..0e02a4334 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -585,10 +585,10 @@ DBMgr::RecordSent( const char* const connName, HostID hid, int nBytes ) { assert( hid >= 0 && hid <= 4 ); const char* fmt = "UPDATE " GAMES_TABLE " SET" - " nsent = nsent + %d, mtimes[%d] = 'now'" + " nsents[%d] = nsents[%d] + %d, mtimes[%d] = 'now'" " WHERE connName = '%s'"; string query; - string_printf( query, fmt, nBytes, hid, connName ); + string_printf( query, fmt, hid, hid, nBytes, hid, connName ); logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() ); execSql( query ); diff --git a/xwords4/relay/xwrelay.sh b/xwords4/relay/xwrelay.sh index 7e60ad072..af28d46ca 100755 --- a/xwords4/relay/xwrelay.sh +++ b/xwords4/relay/xwrelay.sh @@ -53,7 +53,7 @@ cid integer ,nPerDevice INTEGER[] ,seeds INTEGER[] ,ack VARCHAR(1)[] -,nSent INTEGER DEFAULT 0 +,nsents INTEGER[] DEFAULT '{0,0,0,0}' ,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP ,mtimes TIMESTAMP(0)[] ,addrs INET[] From 0447b9f5e5a41d53463cf1119a9539e95ff3707e Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 27 Jun 2013 06:58:17 -0700 Subject: [PATCH 071/126] column name changed --- xwords4/relay/scripts/showinplay.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/scripts/showinplay.sh b/xwords4/relay/scripts/showinplay.sh index f302ba16d..d552532cf 100755 --- a/xwords4/relay/scripts/showinplay.sh +++ b/xwords4/relay/scripts/showinplay.sh @@ -27,7 +27,7 @@ echo -n "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')" echo "; relay pid[s]: $(pidof xwrelay)" echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;") -echo "SELECT dead,connname,cid,room,lang as lg,clntVers as cv ,ntotal as tot,nperdevice as nPerDev,seeds,addrs,tokens,devids,ack,nsent as snt "\ +echo "SELECT dead,connname,cid,room,lang as lg,clntVers as cv ,ntotal as tot,nperdevice as nPerDev,seeds,tokens,ack,nsents as snts "\ "FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \ | psql xwgames From 292982a4b42f70f227612c56629bdc5a9083ce12 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 27 Jun 2013 07:00:36 -0700 Subject: [PATCH 072/126] when splitting packet, queue them and send on timer rather than sleeping the main thread. --- xwords4/linux/linuxmain.c | 118 ++++++++++++++++++++++++++++++-------- xwords4/linux/main.h | 4 +- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index ad92cf36a..ed10753df 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -653,8 +653,8 @@ static CmdInfoRec CmdInfoRecs[] = { ,{ 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_SPLITPACKETS, false, "split-packets", "send tcp packets in " - "sections to test relay reassembly" } + ,{ CMD_SPLITPACKETS, true, "split-packets", "send tcp packets in " + "sections every random MOD seconds to test relay reassembly" } ,{ CMD_CHAT, true, "send-chat", "send a chat every seconds" } #ifdef XWFEATURE_CROSSHAIRS ,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs", @@ -823,6 +823,92 @@ linux_init_relay_socket( CommonGlobals* cGlobals, const CommsAddrRec* addrRec ) return sock; } /* linux_init_relay_socket */ +typedef struct _SendQueueElem { + XP_U32 id; + size_t len; + XP_U8* buf; +} SendQueueElem; + +static bool +send_or_close( CommonGlobals* cGlobals, const XP_U8* buf, size_t len ) +{ + size_t nSent = send( cGlobals->socket, buf, len, 0 ); + bool success = len == nSent; + if ( !success ) { + close( cGlobals->socket ); + (*cGlobals->socketChanged)( cGlobals->socketChangedClosure, + cGlobals->socket, -1, + &cGlobals->storage ); + cGlobals->socket = -1; + + /* delete all pending packets since the socket's bad */ + GSList* iter; + for ( iter = cGlobals->packetQueue; !!iter; iter = iter->next ) { + SendQueueElem* elem = (SendQueueElem*)iter->data; + free( elem->buf ); + free( elem ); + } + } + LOG_RETURNF( "%d", success ); + return success; +} + +static gboolean +sendTimerFired( gpointer data ) +{ + CommonGlobals* cGlobals = (CommonGlobals*)data; + if ( !!cGlobals->packetQueue ) { + guint listLen = g_slist_length( cGlobals->packetQueue ); + assert( 0 < listLen ); + SendQueueElem* elem = (SendQueueElem*)cGlobals->packetQueue->data; + cGlobals->packetQueue = cGlobals->packetQueue->next; + + XP_LOGF( "%s: sending packet %ld of len %d (%d left)", __func__, + elem->id, elem->len, listLen - 1 ); + bool sent = send_or_close( cGlobals, elem->buf, elem->len ); + free( elem->buf ); + free( elem ); + + if ( sent && 1 < listLen ) { + int when = XP_RANDOM() % (1 + cGlobals->params->splitPackets); + (void)g_timeout_add_seconds( when, sendTimerFired, cGlobals ); + } + } + + return FALSE; +} + +static bool +send_per_params( const XP_U8* buf, const XP_U16 buflen, + CommonGlobals* cGlobals ) +{ + bool success; + if ( 0 == cGlobals->params->splitPackets ) { + success = send_or_close( cGlobals, buf, buflen ); + } else { + for ( int nSent = 0; nSent < buflen; ) { + int toSend = buflen / 2; + if ( toSend > buflen - nSent ) { + toSend = buflen - nSent; + } + SendQueueElem* elem = malloc( sizeof(*elem) ); + elem->id = ++cGlobals->nextPacketID; + elem->buf = malloc( toSend ); + XP_MEMCPY( elem->buf, &buf[nSent], toSend ); + elem->len = toSend; + cGlobals->packetQueue = + g_slist_append( cGlobals->packetQueue, elem ); + nSent += toSend; + XP_LOGF( "%s: added packet %ld of len %d", __func__, + elem->id, elem->len ); + } + int when = XP_RANDOM() % (1 + cGlobals->params->splitPackets); + (void)g_timeout_add_seconds( when, sendTimerFired, cGlobals ); + success = TRUE; + } + return success; +} + static XP_S16 linux_tcp_send( const XP_U8* buf, XP_U16 buflen, CommonGlobals* globals, const CommsAddrRec* addrRec ) @@ -842,29 +928,13 @@ linux_tcp_send( const XP_U8* buf, XP_U16 buflen, if ( sock != -1 ) { XP_U16 netLen = htons( buflen ); - errno = 0; + XP_U8 tmp[buflen + sizeof(netLen)]; + XP_MEMCPY( &tmp[0], &netLen, sizeof(netLen) ); + XP_MEMCPY( &tmp[sizeof(netLen)], buf, buflen ); - result = send( sock, &netLen, sizeof(netLen), 0 ); - if ( result == sizeof(netLen) ) { - if ( globals->params->splitPackets ) { - int halflen = buflen / 2; - result = send( sock, buf, halflen, 0 ); - sleep( 1 ); - result += send( sock, buf + halflen, buflen - halflen, 0 ); - } else { - result = send( sock, buf, buflen, 0 ); - } + if ( send_per_params( tmp, buflen + sizeof(netLen), globals ) ) { + result = buflen; } - if ( result <= 0 ) { - XP_STATUSF( "closing non-functional socket" ); - close( sock ); - (*globals->socketChanged)( globals->socketChangedClosure, - sock, -1, &globals->storage ); - globals->socket = -1; - } - - XP_STATUSF( "%s: send(sock=%d) returned %d of %d (err=%d)", - __func__, sock, result, buflen, errno ); } else { XP_LOGF( "%s: socket still -1", __func__ ); } @@ -1922,7 +1992,7 @@ main( int argc, char** argv ) mainParams.allowPeek = XP_FALSE; break; case CMD_SPLITPACKETS: - mainParams.splitPackets = XP_TRUE; + mainParams.splitPackets = atoi( optarg ); break; case CMD_CHAT: mainParams.chatsInterval = atoi(optarg); diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index bda26c923..fa354b52c 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -90,7 +90,7 @@ typedef struct LaunchParams { XP_Bool skipGameOver; XP_Bool useMmap; XP_Bool closeStdin; - XP_Bool splitPackets; + XP_U16 splitPackets; XP_U16 chatsInterval; /* 0 means disabled */ #ifdef XWFEATURE_SEARCHLIMIT XP_Bool allowHintRect; @@ -176,6 +176,8 @@ struct CommonGlobals { SocketChangedFunc socketChanged; void* socketChangedClosure; + GSList* packetQueue; + XP_U32 nextPacketID; /* for debugging */ CommsRelayState state; From 4a49c49e8770b54bbdce349f58d44a2f775a2530 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 27 Jun 2013 07:58:02 -0700 Subject: [PATCH 073/126] changes for second tablet fix release. This time I've confirmed they work on the google store. --- xwords4/android/XWords4/AndroidManifest.xml | 13 ++----------- xwords4/android/XWords4/res/raw/changes | 6 +++--- xwords4/android/XWords4/res/values/app_name.xml | 2 +- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 4e8c9b55e..3ce031197 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> @@ -45,16 +45,7 @@ - - - - diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index ee10ed83e..ab5507aca 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -5,13 +5,13 @@ -Crosswords 4.4 beta 61 release +Crosswords 4.4 beta 62 release

New with this release

    -
  • Change manifest to make SMS optional, allowing installation on - tablets and other devices without phones.
  • +
  • Change manifest to make phone hardware optional, allowing + installation on tablets and other devices without phones.
diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 332550f5b..0b191a33f 100644 --- a/xwords4/android/XWords4/res/values/app_name.xml +++ b/xwords4/android/XWords4/res/values/app_name.xml @@ -1,5 +1,5 @@ - 4.4 beta 61 + 4.4 beta 62 From 0a794f390f597447d8b8411c9e5d5ee7fb22ba76 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 18:47:11 -0700 Subject: [PATCH 074/126] fix off-by-one error leading to malformed queries --- xwords4/relay/xwrelay.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 05c7a8ece..d6572102c 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -1428,17 +1428,17 @@ void string_printf( string& str, const char* fmt, ... ) { const int origsiz = str.size(); - int newsiz = 100; + int addsiz = 100; va_list ap; for ( ; ; ) { - str.resize( origsiz + newsiz ); + str.resize( origsiz + addsiz ); va_start( ap, fmt ); - int len = vsnprintf( (char *)str.c_str() + origsiz, newsiz, fmt, ap ); + int len = vsnprintf( (char *)str.c_str() + origsiz, addsiz, fmt, ap ); va_end( ap ); - if ( len > newsiz ) { // needs more space - newsiz = len + 1; + if ( len >= addsiz ) { // needs more space + addsiz = len + 1; } else if ( -1 == len ) { assert(0); // should be impossible } else { From 8419bf2a04e45720ee7662a481a5cc386acb9157 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 18:48:57 -0700 Subject: [PATCH 075/126] close socket if length == 0 -- protocol violation or network error --- xwords4/relay/udpqueue.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index d06d29e42..081210b33 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -103,6 +103,7 @@ bool UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) { PartialPacket* packet; + bool success = true; int sock = addr->socket(); @@ -122,10 +123,11 @@ UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) if ( packet->readSoFar() < sizeof( packet->m_len ) ) { if ( packet->readAtMost( sizeof(packet->m_len) - packet->readSoFar() ) ) { packet->m_len = ntohs(*(unsigned short*)packet->data()); + success = 0 < packet->m_len; } } - if ( packet->readSoFar() >= sizeof( packet->m_len ) ) { + if ( success && packet->readSoFar() >= sizeof( packet->m_len ) ) { assert( 0 < packet->m_len ); int leftToRead = packet->m_len - (packet->readSoFar() - sizeof(packet->m_len)); @@ -137,7 +139,8 @@ UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) } } - return NULL == packet || packet->stillGood(); + success = success && (NULL == packet || packet->stillGood()); + return success; } void From 5c284fe6b0ddd338cb6b67db03f934f4671cc704 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 18:55:19 -0700 Subject: [PATCH 076/126] test PID is live before killing it --- xwords4/linux/scripts/discon_ok2.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 1bd691656..a0625550b 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -204,6 +204,8 @@ build_cmds() { PARAMS="$PARAMS --game-dict $DICT --port $PORT --host $HOST " PARAMS="$PARAMS --file $FILE --slow-robot 1:3 --skip-confirm" PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS" + # PARAMS="$PARAMS --split-packets 2" + # PARAMS="$PARAMS --send-chat 2" # PARAMS="$PARAMS --savefail-pct 10" [ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM" PARAMS="$PARAMS $PUBLIC" @@ -461,10 +463,13 @@ run_cmds() { ROOM_PIDS[$ROOM]=$PID MINEND[$KEY]=$(($NOW + $MINRUN)) else - SLEEP=$((${MINEND[$KEY]} - $NOW)) - [ $SLEEP -gt 0 ] && sleep $SLEEP - kill ${PIDS[$KEY]} || true - wait ${PIDS[$KEY]} + PID=${PIDS[$KEY]} + if [ -d /proc/$PID ]; then + SLEEP=$((${MINEND[$KEY]} - $NOW)) + [ $SLEEP -gt 0 ] && sleep $SLEEP + kill $PID || true + wait $PID + fi PIDS[$KEY]=0 ROOM_PIDS[$ROOM]=0 [ "$DROP_N" -ge 0 ] && increment_drop $KEY From 6c756533e3a2ebe1f33cd580a218fb1ffaa5f09c Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 20:32:19 -0700 Subject: [PATCH 077/126] fix double-dispose crash (but there's still a memory leak) --- xwords4/linux/linuxmain.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index ed10753df..53288984e 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -829,6 +829,14 @@ typedef struct _SendQueueElem { XP_U8* buf; } SendQueueElem; +static void +free_elem_proc( gpointer data ) +{ + SendQueueElem* elem = (SendQueueElem*)data; + free( elem->buf ); + free( elem ); +} + static bool send_or_close( CommonGlobals* cGlobals, const XP_U8* buf, size_t len ) { @@ -842,12 +850,8 @@ send_or_close( CommonGlobals* cGlobals, const XP_U8* buf, size_t len ) cGlobals->socket = -1; /* delete all pending packets since the socket's bad */ - GSList* iter; - for ( iter = cGlobals->packetQueue; !!iter; iter = iter->next ) { - SendQueueElem* elem = (SendQueueElem*)iter->data; - free( elem->buf ); - free( elem ); - } + g_slist_free_full( cGlobals->packetQueue, free_elem_proc ); + cGlobals->packetQueue = NULL; } LOG_RETURNF( "%d", success ); return success; @@ -1056,7 +1060,7 @@ blocking_read( int fd, unsigned char* buf, int len ) { int nRead = 0; while ( nRead < len ) { - XP_LOGF( "%s: blocking for %d bytes", __func__, len ); + XP_LOGF( "%s(fd=%d): blocking for %d bytes", __func__, fd, len ); ssize_t siz = read( fd, buf + nRead, len - nRead ); if ( siz <= 0 ) { XP_LOGF( "read => %d, errno=%d (\"%s\")", nRead, From 17eda4e5e111ef3eb62b0d802c39eeec2ade174e Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 20:33:12 -0700 Subject: [PATCH 078/126] move md5sum code into separate function that relay can copy --- xwords4/linux/linuxdict.c | 15 ++++++++------- xwords4/linux/linuxutl.c | 15 +++++++++++++++ xwords4/linux/linuxutl.h | 4 ++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/xwords4/linux/linuxdict.c b/xwords4/linux/linuxdict.c index b6e207768..653c19fb5 100644 --- a/xwords4/linux/linuxdict.c +++ b/xwords4/linux/linuxdict.c @@ -32,6 +32,7 @@ #include "dictnryp.h" #include "linuxmain.h" #include "strutils.h" +#include "linuxutl.h" typedef struct DictStart { XP_U32 numNodes; @@ -374,16 +375,16 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const LaunchParams* params, ) { XP_U32 curPos = ptr - dctx->dictBase; gssize dictLength = dctx->dictLength - curPos; - GChecksum* cksum = g_checksum_new( G_CHECKSUM_MD5 ); - g_checksum_update( cksum, ptr, dictLength ); - const gchar* sum = g_checksum_get_string( cksum ); - XP_LOGF( "calculated sum on %d bytes: %s", dictLength, sum ); + + XP_UCHAR buf[128]; + XP_U16 buflen = VSIZE(buf); + figureMD5Sum( ptr, dictLength, buf, &buflen ); + assert( buflen < VSIZE(buf) ); if ( NULL == dctx->super.md5Sum ) { - dctx->super.md5Sum = copyString( dctx->super.mpool, sum ); + dctx->super.md5Sum = copyString( dctx->super.mpool, buf ); } else { - XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, sum ) ); + XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, buf ) ); } - g_checksum_free( cksum ); } dctx->super.nFaces = numFaces; diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index 650c0c892..db67e3c4f 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -651,6 +651,21 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd ) cGlobals->noConnMsgs = NULL; } /* writeNoConnMsgs */ +void +figureMD5Sum( const XP_U8* data, XP_U16 datalen, XP_UCHAR* buf, XP_U16* buflen ) +{ + GChecksum* cksum = g_checksum_new( G_CHECKSUM_MD5 ); + g_checksum_update( cksum, data, datalen ); + const gchar* sum = g_checksum_get_string( cksum ); + XP_LOGF( "%s calculated sum on %d bytes: %s", __func__, datalen, sum ); + int sumlen = strlen( sum ); + if ( sumlen < *buflen ) { + (void)snprintf( buf, *buflen, "%s", sum ); + } + *buflen = sumlen; + g_checksum_free( cksum ); +} + #ifdef TEXT_MODEL /* This is broken for UTF-8, even Spanish */ void diff --git a/xwords4/linux/linuxutl.h b/xwords4/linux/linuxutl.h index 781031a32..967bd03fd 100644 --- a/xwords4/linux/linuxutl.h +++ b/xwords4/linux/linuxutl.h @@ -49,6 +49,10 @@ XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len, const XP_UCHAR* relayID ); void writeNoConnMsgs( CommonGlobals* cGlobals, int fd ); +void figureMD5Sum( const XP_U8* data, XP_U16 datalen, + XP_UCHAR* buf, XP_U16* buflen ); + + #ifdef STREAM_VERS_BIGBOARD void setSquareBonuses( const CommonGlobals* cGlobals ); #else From 1a73ab26acdd39a8f76d5dd973e80be29bbb201e Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 28 Jun 2013 20:33:27 -0700 Subject: [PATCH 079/126] tweak logging --- xwords4/linux/cursesmain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 2e72e3b44..e75608c65 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1673,7 +1673,8 @@ relay_connd_curses( void* XP_UNUSED(closure), XP_UCHAR* const XP_UNUSED(room), XP_Bool XP_UNUSED_DBG(allHere), XP_U16 XP_UNUSED_DBG(nMissing) ) { - XP_LOGF( "%s got allHere: %d; nMissing: %d", __func__, allHere, nMissing ); + XP_LOGF( "%s got allHere: %s; nMissing: %d", __func__, + allHere?"true":"false", nMissing ); } static void From 7efbfab0e5de104b56f1b69dbfbe032a5f104230 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 06:50:57 -0700 Subject: [PATCH 080/126] fix crash in debug version: len of data to be summed needs to be 32 bits! --- xwords4/linux/linuxutl.c | 2 +- xwords4/linux/linuxutl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index db67e3c4f..4a48e1901 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -652,7 +652,7 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd ) } /* writeNoConnMsgs */ void -figureMD5Sum( const XP_U8* data, XP_U16 datalen, XP_UCHAR* buf, XP_U16* buflen ) +figureMD5Sum( const XP_U8* data, gssize datalen, XP_UCHAR* buf, XP_U16* buflen ) { GChecksum* cksum = g_checksum_new( G_CHECKSUM_MD5 ); g_checksum_update( cksum, data, datalen ); diff --git a/xwords4/linux/linuxutl.h b/xwords4/linux/linuxutl.h index 967bd03fd..1fe0c3737 100644 --- a/xwords4/linux/linuxutl.h +++ b/xwords4/linux/linuxutl.h @@ -49,7 +49,7 @@ XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len, const XP_UCHAR* relayID ); void writeNoConnMsgs( CommonGlobals* cGlobals, int fd ); -void figureMD5Sum( const XP_U8* data, XP_U16 datalen, +void figureMD5Sum( const XP_U8* data, gssize datalen, XP_UCHAR* buf, XP_U16* buflen ); From 6fbf2d4a8b89f520c2a1edd3d57142b57ee0a981 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 07:36:56 -0700 Subject: [PATCH 081/126] use g_compute_checksum_for_data, removing self-written equivalent --- xwords4/linux/linuxdict.c | 10 ++++------ xwords4/linux/linuxutl.c | 15 --------------- xwords4/linux/linuxutl.h | 4 ---- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/xwords4/linux/linuxdict.c b/xwords4/linux/linuxdict.c index 653c19fb5..865f3d0ba 100644 --- a/xwords4/linux/linuxdict.c +++ b/xwords4/linux/linuxdict.c @@ -376,15 +376,13 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const LaunchParams* params, XP_U32 curPos = ptr - dctx->dictBase; gssize dictLength = dctx->dictLength - curPos; - XP_UCHAR buf[128]; - XP_U16 buflen = VSIZE(buf); - figureMD5Sum( ptr, dictLength, buf, &buflen ); - assert( buflen < VSIZE(buf) ); + gchar* checksum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, dictLength ); if ( NULL == dctx->super.md5Sum ) { - dctx->super.md5Sum = copyString( dctx->super.mpool, buf ); + dctx->super.md5Sum = copyString( dctx->super.mpool, checksum ); } else { - XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, buf ) ); + XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, checksum ) ); } + g_free( checksum ); } dctx->super.nFaces = numFaces; diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index 4a48e1901..650c0c892 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -651,21 +651,6 @@ writeNoConnMsgs( CommonGlobals* cGlobals, int fd ) cGlobals->noConnMsgs = NULL; } /* writeNoConnMsgs */ -void -figureMD5Sum( const XP_U8* data, gssize datalen, XP_UCHAR* buf, XP_U16* buflen ) -{ - GChecksum* cksum = g_checksum_new( G_CHECKSUM_MD5 ); - g_checksum_update( cksum, data, datalen ); - const gchar* sum = g_checksum_get_string( cksum ); - XP_LOGF( "%s calculated sum on %d bytes: %s", __func__, datalen, sum ); - int sumlen = strlen( sum ); - if ( sumlen < *buflen ) { - (void)snprintf( buf, *buflen, "%s", sum ); - } - *buflen = sumlen; - g_checksum_free( cksum ); -} - #ifdef TEXT_MODEL /* This is broken for UTF-8, even Spanish */ void diff --git a/xwords4/linux/linuxutl.h b/xwords4/linux/linuxutl.h index 1fe0c3737..781031a32 100644 --- a/xwords4/linux/linuxutl.h +++ b/xwords4/linux/linuxutl.h @@ -49,10 +49,6 @@ XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len, const XP_UCHAR* relayID ); void writeNoConnMsgs( CommonGlobals* cGlobals, int fd ); -void figureMD5Sum( const XP_U8* data, gssize datalen, - XP_UCHAR* buf, XP_U16* buflen ); - - #ifdef STREAM_VERS_BIGBOARD void setSquareBonuses( const CommonGlobals* cGlobals ); #else From 195f873ab03337b98ab4acb8667aaad8c293d5a1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 07:37:35 -0700 Subject: [PATCH 082/126] tweak logging --- xwords4/linux/cursesmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index e75608c65..bd8f0ffdf 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1732,7 +1732,7 @@ chatsTimerFired( gpointer data ) gettimeofday( &tv, &tz ); timp = localtime( &tv.tv_sec ); - snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d:", + snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d", timp->tm_hour, timp->tm_min, timp->tm_sec ); server_sendChat( globals->cGlobals.game.server, msg ); XP_LOGF( "%s: sent \"%s\"", __func__, msg ); From c77021231c32119f2262465df069df10e50c6f55 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 08:33:29 -0700 Subject: [PATCH 083/126] print checksums of sent and received messages --- xwords4/common/comms.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index b0578dbda..729cabe31 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1204,6 +1204,11 @@ sendMsg( CommsCtxt* comms, MsgQueueElem* elem ) channelNo = elem->channelNo; +#ifdef COMMS_CHECKSUM + XP_LOGF( "%s: sending message of len %d with sum %s", __func__, elem->len, + elem->checksum ); +#endif + if ( 0 ) { #ifdef XWFEATURE_RELAY } else if ( conType == COMMS_CONN_RELAY ) { @@ -1809,6 +1814,9 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream, XP_Bool usingRelay = XP_FALSE; XP_ASSERT( retAddr == NULL || comms->addr.conType == retAddr->conType ); +#ifdef COMMS_CHECKSUM + XP_U16 initialLen = stream_getSize( stream ); +#endif if ( !preProcess( comms, stream, &usingRelay, &senderID ) ) { XP_U32 connID; @@ -1816,6 +1824,17 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream, MsgID msgID; MsgID lastMsgRcd; +#ifdef COMMS_CHECKSUM + { + XP_U16 len = stream_getSize( stream ); + // stream_getPtr pts at base, but sum excludes relay header + const XP_U8* ptr = initialLen - len + stream_getPtr( stream ); + gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, len ); + XP_LOGF( "%s: got message of len %d with sum %s", __func__, len, sum ); + g_free( sum ); + } +#endif + /* reject too-small message */ if ( stream_getSize( stream ) >= (sizeof(connID) + sizeof(channelNo) From b506a91068f6279a1af6e6abb7f56c931059124a Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 08:33:58 -0700 Subject: [PATCH 084/126] add commandline params --- xwords4/linux/scripts/discon_ok2.sh | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index a0625550b..c5dba6205 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -26,6 +26,7 @@ SEED="" BOARD_SIZES_OLD=(15) BOARD_SIZES_NEW=(15) NAMES=(UNUSED Brynn Ariela Kati Eric) +SEND_CHAT='' declare -A PIDS declare -A APPS @@ -49,8 +50,14 @@ function cleanup() { sleep 1 done echo "cleaning everything up...." - rm -f $(dirname $0)/../../relay/xwrelay.log - rm -rf ${LOGDIR} + if [ -d $LOGDIR ]; then + mv $LOGDIR /tmp/${LOGDIR}_$$ + fi + if [ -e $(dirname $0)/../../relay/xwrelay.log ]; then + mkdir -p /tmp/${LOGDIR}_$$ + mv $(dirname $0)/../../relay/xwrelay.log /tmp/${LOGDIR}_$$ + fi + echo "delete from games;" | psql -q -t xwgames } @@ -205,7 +212,9 @@ build_cmds() { PARAMS="$PARAMS --file $FILE --slow-robot 1:3 --skip-confirm" PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS" # PARAMS="$PARAMS --split-packets 2" - # PARAMS="$PARAMS --send-chat 2" + if [ -n $SEND_CHAT ]; then + PARAMS="$PARAMS --send-chat $SEND_CHAT" + fi # PARAMS="$PARAMS --savefail-pct 10" [ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM" PARAMS="$PARAMS $PUBLIC" @@ -549,6 +558,8 @@ function usage() { echo " [--port ] \\" >&2 echo " [--seed ] \\" >&2 echo " [--undo-pct ] \\" >&2 + echo " [--send-chat \\" >&2 + echo " [--resign-ratio <0 <= n <=1000 > \\" >&2 echo " [--help] \\" >&2 exit 1 @@ -615,6 +626,14 @@ while [ "$#" -gt 0 ]; do UNDO_PCT=$(getArg $*) shift ;; + --send-chat) + SEND_CHAT=$(getArg $*) + shift + ;; + --resign-ratio) + RESIGN_RATIO=$(getArg $*) + shift + ;; --help) usage ;; From 070590b3c2b8351d9231f2cb35b7ee3961751d56 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 08:34:09 -0700 Subject: [PATCH 085/126] cleanup --- xwords4/linux/linuxdict.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/xwords4/linux/linuxdict.c b/xwords4/linux/linuxdict.c index 865f3d0ba..021da9a3e 100644 --- a/xwords4/linux/linuxdict.c +++ b/xwords4/linux/linuxdict.c @@ -1,6 +1,6 @@ /* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */ /* - * Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights + * Copyright 1997 - 2013 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -93,8 +93,6 @@ getNullTermParam( LinuxDictionaryCtxt* XP_UNUSED_DBG(dctx), const XP_U8** ptr, XP_U16 len = 1 + XP_STRLEN( (XP_UCHAR*)*ptr ); XP_UCHAR* result = XP_MALLOC( dctx->super.mpool, len ); XP_MEMCPY( result, *ptr, len ); - XP_LOGF( "%s: got param of len %d: \"%s\"", __func__, - len, result ); *ptr += len; *headerLen -= len; return result; @@ -376,13 +374,13 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const LaunchParams* params, XP_U32 curPos = ptr - dctx->dictBase; gssize dictLength = dctx->dictLength - curPos; - gchar* checksum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, dictLength ); + gchar* checksum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, dictLength ); if ( NULL == dctx->super.md5Sum ) { dctx->super.md5Sum = copyString( dctx->super.mpool, checksum ); } else { XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, checksum ) ); } - g_free( checksum ); + g_free( checksum ); } dctx->super.nFaces = numFaces; From 9ceb1615b3f2c11ffe2bfc0a3b58853af013fe73 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 30 Jun 2013 17:24:33 -0700 Subject: [PATCH 086/126] replace tabs with spaces --- xwords4/linux/scripts/discon_ok2.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index c5dba6205..77082de60 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -51,11 +51,11 @@ function cleanup() { done echo "cleaning everything up...." if [ -d $LOGDIR ]; then - mv $LOGDIR /tmp/${LOGDIR}_$$ + mv $LOGDIR /tmp/${LOGDIR}_$$ fi if [ -e $(dirname $0)/../../relay/xwrelay.log ]; then - mkdir -p /tmp/${LOGDIR}_$$ - mv $(dirname $0)/../../relay/xwrelay.log /tmp/${LOGDIR}_$$ + mkdir -p /tmp/${LOGDIR}_$$ + mv $(dirname $0)/../../relay/xwrelay.log /tmp/${LOGDIR}_$$ fi echo "delete from games;" | psql -q -t xwgames @@ -212,9 +212,9 @@ build_cmds() { PARAMS="$PARAMS --file $FILE --slow-robot 1:3 --skip-confirm" PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS" # PARAMS="$PARAMS --split-packets 2" - if [ -n $SEND_CHAT ]; then - PARAMS="$PARAMS --send-chat $SEND_CHAT" - fi + if [ -n $SEND_CHAT ]; then + PARAMS="$PARAMS --send-chat $SEND_CHAT" + fi # PARAMS="$PARAMS --savefail-pct 10" [ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM" PARAMS="$PARAMS $PUBLIC" @@ -626,14 +626,14 @@ while [ "$#" -gt 0 ]; do UNDO_PCT=$(getArg $*) shift ;; - --send-chat) - SEND_CHAT=$(getArg $*) - shift - ;; - --resign-ratio) - RESIGN_RATIO=$(getArg $*) - shift - ;; + --send-chat) + SEND_CHAT=$(getArg $*) + shift + ;; + --resign-ratio) + RESIGN_RATIO=$(getArg $*) + shift + ;; --help) usage ;; From c202cd8608e8ae7dafb6db14e585aecf34f6031c Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 1 Jul 2013 07:51:38 -0700 Subject: [PATCH 087/126] don't pile on chat messages when relay isn't even connected: it's an unfair test that doesn't duplicate what users will likely do. --- xwords4/linux/cursesmain.c | 13 ++++++------- xwords4/linux/cursesmain.h | 1 + xwords4/linux/linuxmain.c | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index bd8f0ffdf..2614dd0e7 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1661,9 +1661,10 @@ relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len, } /* relay_sendNoConn_curses */ static void -relay_status_curses( void* XP_UNUSED(closure), - CommsRelayState XP_UNUSED_DBG(state) ) +relay_status_curses( void* closure, CommsRelayState state ) { + CursesAppGlobals* globals = (CursesAppGlobals*)closure; + globals->commsRelayState = state; XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) ); } @@ -1720,22 +1721,20 @@ chatsTimerFired( gpointer data ) { CursesAppGlobals* globals = (CursesAppGlobals*)data; - GameStateInfo gsi; - game_getState( &globals->cGlobals.game, &gsi ); - - if ( gsi.gameIsConnected ) { + if ( COMMS_RELAYSTATE_ALLCONNECTED == globals->commsRelayState ) { XP_UCHAR msg[128]; struct tm* timp; struct timeval tv; struct timezone tz; + XP_LOGF( "%s: sending \"%s\"", __func__, msg ); + gettimeofday( &tv, &tz ); timp = localtime( &tv.tv_sec ); snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d", timp->tm_hour, timp->tm_min, timp->tm_sec ); server_sendChat( globals->cGlobals.game.server, msg ); - XP_LOGF( "%s: sent \"%s\"", __func__, msg ); } return TRUE; diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index f9dd34cd6..99f771730 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -81,6 +81,7 @@ struct CursesAppGlobals { short statusLine; XWGameState state; + CommsRelayState commsRelayState; struct sockaddr_in listenerSockAddr; #ifdef USE_GLIBLOOP diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 53288984e..aa1f6e868 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -48,8 +48,6 @@ # include #endif -/* #include */ - #include "linuxmain.h" #include "linuxutl.h" #include "linuxbt.h" From 2da26cf8a9573dc9282369461f36acc9138e5f4c Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 1 Jul 2013 07:52:02 -0700 Subject: [PATCH 088/126] cleanup/comment --- xwords4/relay/udpqueue.cpp | 6 ++++-- xwords4/relay/udpqueue.h | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index 081210b33..488521a6f 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -1,7 +1,7 @@ /* -*- compile-command: "make -k -j3"; -*- */ /* - * Copyright 2010-2012 by Eric House (xwords@eehouse.org). All rights + * Copyright 2010-2013 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -122,7 +122,9 @@ UdpQueue::handle( const AddrInfo* addr, QueueCallback cb ) // First see if we've read the length bytes if ( packet->readSoFar() < sizeof( packet->m_len ) ) { if ( packet->readAtMost( sizeof(packet->m_len) - packet->readSoFar() ) ) { - packet->m_len = ntohs(*(unsigned short*)packet->data()); + uint16_t tmp; + memcpy( &tmp, packet->data(), sizeof(tmp) ); + packet->m_len = ntohs(tmp); success = 0 < packet->m_len; } } diff --git a/xwords4/relay/udpqueue.h b/xwords4/relay/udpqueue.h index 12307646a..16060feda 100644 --- a/xwords4/relay/udpqueue.h +++ b/xwords4/relay/udpqueue.h @@ -71,17 +71,17 @@ public: class PartialPacket { public: - PartialPacket(int sock) { - m_sock = sock; - m_len = 0; - m_errno = 0; - } + PartialPacket(int sock) + :m_len(0) + ,m_sock(sock) + ,m_errno(0) + {} bool stillGood() const ; bool readAtMost( int len ); size_t readSoFar() const { return m_buf.size(); } const uint8_t* data() const { return m_buf.data(); } - unsigned short m_len; + unsigned short m_len; /* decoded via ntohs from the first 2 bytes */ private: vector m_buf; From 1f63745e34adf77f9b5fed46dd5ea2d30eb3e7c0 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 1 Jul 2013 07:54:38 -0700 Subject: [PATCH 089/126] add more logging in search of channelNo bug --- xwords4/common/comms.c | 10 ++++++++-- xwords4/common/server.c | 11 ++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 729cabe31..18825e368 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -287,7 +287,9 @@ static void init_relay( CommsCtxt* comms, XP_U16 nPlayersHere, XP_U16 nPlayersTotal ) { comms->r.myHostID = comms->isServer? HOST_ID_SERVER: HOST_ID_NONE; - XP_LOGF( "%s: set hostid: %x", __func__, comms->r.myHostID ); + if ( HOST_ID_NONE != comms->r.myHostID ) { + XP_LOGF( "%s: set hostid: %x", __func__, comms->r.myHostID ); + } set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED ); comms->r.nPlayersHere = nPlayersHere; comms->r.nPlayersTotal = nPlayersTotal; @@ -953,6 +955,8 @@ static MsgQueueElem* makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec, XP_PlayerAddr channelNo, XWStreamCtxt* stream ) { + XP_LOGF( "%s(channelNo=%x)", __func__, channelNo ); + // XP_ASSERT( 0 == (channelNo & CHANNEL_MASK) ); XP_U16 headerLen; XP_U16 streamSize = NULL == stream? 0 : stream_getSize( stream ); MsgID lastMsgSaved = (!!rec)? rec->lastMsgSaved : 0; @@ -1634,7 +1638,7 @@ preProcess( CommsCtxt* comms, XWStreamCtxt* stream, static AddressRecord* getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr, - XP_PlayerAddr channelNo, XP_Bool maskChannel ) + const XP_PlayerAddr channelNo, XP_Bool maskChannel ) { CommsConnType conType; AddressRecord* rec; @@ -1688,6 +1692,8 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr, break; } } + XP_LOGF( "%s(channelNo=%x, maskChannel=%s) => %p", __func__, + channelNo, maskChannel? "true":"false", rec ); return rec; } /* getRecordFor */ diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 98e47cc39..8d2bec17e 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -1171,13 +1171,17 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) RemoteAddress* addr; addr = &server->nv.addresses[server->nv.nDevices]; - deviceIndex = server->nv.nDevices++; - XP_ASSERT( channelNo != 0 ); addr->channelNo = channelNo; + XP_LOGF( "%s: set channelNo to %x for device %d", __func__, + channelNo, server->nv.nDevices ); + + deviceIndex = server->nv.nDevices++; #ifdef STREAM_VERS_BIGBOARD addr->streamVersion = STREAM_SAVE_PREVWORDS; #endif + } else { + XP_LOGF( "%s: deviceIndex already set", __func__ ); } player->deviceIndex = deviceIndex; @@ -1272,11 +1276,12 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) channelNo = stream_getAddress( stream ); XP_ASSERT( channelNo != 0 ); server->nv.addresses[0].channelNo = channelNo; + XP_LOGF( "%s: assigning channelNo %x for 0", __func__, channelNo ); model_setSize( model, nCols ); nPlayers = localGI.nPlayers; - XP_STATUSF( "reading in %d players", localGI.nPlayers ); + XP_LOGF( "%s: reading in %d players", __func__, localGI.nPlayers ); gi_disposePlayerInfo( MPPARM(server->mpool) &localGI ); From 3d18e5832eb96aaaa0d2714781b5df330290ba56 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 05:46:48 -0700 Subject: [PATCH 090/126] specify server's port in conf file since at least on one of my machines it's not what libpq expects. (Required by upgrade from 8.4 to 9.1.) --- xwords4/relay/dbmgr.cpp | 19 ++++++++++++++----- xwords4/relay/xwrelay.conf_tmplate | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 0e02a4334..e80efde06 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -757,7 +757,9 @@ DBMgr::execSql( const char* const query ) PGresult* result = PQexec( getThreadConn(), query ); bool ok = PGRES_COMMAND_OK == PQresultStatus(result); if ( !ok ) { - logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result) ); + logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), + PQresultErrorMessage(result) ); + assert( 0 ); } PQclear( result ); return ok; @@ -1143,12 +1145,19 @@ DBMgr::getThreadConn( void ) if ( NULL == conn ) { char buf[128]; - int len = snprintf( buf, sizeof(buf), "dbname = " ); - if ( !RelayConfigs::GetConfigs()-> - GetValueFor( "DB_NAME", &buf[len], sizeof(buf)-len ) ) { + int port; + if ( !RelayConfigs::GetConfigs()->GetValueFor( "DB_NAME", buf, + sizeof(buf) ) ) { assert( 0 ); } - conn = PQconnectdb( buf ); + if ( !RelayConfigs::GetConfigs()->GetValueFor( "DB_PORT", &port ) ) { + assert( 0 ); + } + string params; + string_printf( params, "dbname = %s ", buf ); + string_printf( params, "port = %d ", port ); + + conn = PQconnectdb( params.c_str() ); pthread_setspecific( m_conn_key, conn ); } return conn; diff --git a/xwords4/relay/xwrelay.conf_tmplate b/xwords4/relay/xwrelay.conf_tmplate index 2773342e0..2a491c0c2 100644 --- a/xwords4/relay/xwrelay.conf_tmplate +++ b/xwords4/relay/xwrelay.conf_tmplate @@ -56,6 +56,8 @@ SERVERNAME=eehouse.org # name of the database. (Table names are hard-coded.) DB_NAME=xwgames +# UDP port postgres server is listening on +DB_PORT=5433 # Initial level of logging. See xwrelay_priv.h for values. Currently # 0 means errors only, 1 info, 2 verbose and 3 very verbose. From 9fca3792cf6b6f5ccdc74c633e3da236f3bb1acc Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 1 Jul 2013 07:51:38 -0700 Subject: [PATCH 091/126] don't pile on chat messages when relay isn't even connected: it's an unfair test that doesn't duplicate what users will likely do. --- xwords4/linux/cursesmain.c | 13 ++++++------- xwords4/linux/cursesmain.h | 1 + xwords4/linux/linuxmain.c | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index bd8f0ffdf..2614dd0e7 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1661,9 +1661,10 @@ relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len, } /* relay_sendNoConn_curses */ static void -relay_status_curses( void* XP_UNUSED(closure), - CommsRelayState XP_UNUSED_DBG(state) ) +relay_status_curses( void* closure, CommsRelayState state ) { + CursesAppGlobals* globals = (CursesAppGlobals*)closure; + globals->commsRelayState = state; XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) ); } @@ -1720,22 +1721,20 @@ chatsTimerFired( gpointer data ) { CursesAppGlobals* globals = (CursesAppGlobals*)data; - GameStateInfo gsi; - game_getState( &globals->cGlobals.game, &gsi ); - - if ( gsi.gameIsConnected ) { + if ( COMMS_RELAYSTATE_ALLCONNECTED == globals->commsRelayState ) { XP_UCHAR msg[128]; struct tm* timp; struct timeval tv; struct timezone tz; + XP_LOGF( "%s: sending \"%s\"", __func__, msg ); + gettimeofday( &tv, &tz ); timp = localtime( &tv.tv_sec ); snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d", timp->tm_hour, timp->tm_min, timp->tm_sec ); server_sendChat( globals->cGlobals.game.server, msg ); - XP_LOGF( "%s: sent \"%s\"", __func__, msg ); } return TRUE; diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index f9dd34cd6..99f771730 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -81,6 +81,7 @@ struct CursesAppGlobals { short statusLine; XWGameState state; + CommsRelayState commsRelayState; struct sockaddr_in listenerSockAddr; #ifdef USE_GLIBLOOP diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 53288984e..aa1f6e868 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -48,8 +48,6 @@ # include #endif -/* #include */ - #include "linuxmain.h" #include "linuxutl.h" #include "linuxbt.h" From dd48cea912dfb860b552647750293f53d77aede4 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 07:26:32 -0700 Subject: [PATCH 092/126] don't log string before it's initialized --- xwords4/linux/cursesmain.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 2614dd0e7..e87955aff 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1727,13 +1727,12 @@ chatsTimerFired( gpointer data ) struct timeval tv; struct timezone tz; - XP_LOGF( "%s: sending \"%s\"", __func__, msg ); - gettimeofday( &tv, &tz ); timp = localtime( &tv.tv_sec ); snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d", timp->tm_hour, timp->tm_min, timp->tm_sec ); + XP_LOGF( "%s: sending \"%s\"", __func__, msg ); server_sendChat( globals->cGlobals.game.server, msg ); } From b53f9bc578c818d352ccb1fbb90d6a01827e1048 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 07:27:31 -0700 Subject: [PATCH 093/126] let Pqexec fail three times, usleeping between, before restarting with an assert --- xwords4/relay/dbmgr.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index e80efde06..f1b173404 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -754,14 +754,19 @@ DBMgr::execSql( const string& query ) bool DBMgr::execSql( const char* const query ) { - PGresult* result = PQexec( getThreadConn(), query ); - bool ok = PGRES_COMMAND_OK == PQresultStatus(result); - if ( !ok ) { - logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), - PQresultErrorMessage(result) ); - assert( 0 ); + bool ok = false; + for ( int ii = 0; !ok && ii < 3; ++ii ) { + PGresult* result = PQexec( getThreadConn(), query ); + ok = PGRES_COMMAND_OK == PQresultStatus(result); + if ( !ok ) { + logf( XW_LOGERROR, "%s: PQexec=>%s;%s", __func__, + PQresStatus(PQresultStatus(result)), + PQresultErrorMessage(result) ); + usleep( 20000 ); + } + PQclear( result ); } - PQclear( result ); + assert( ok ); return ok; } From 0ac5d6d9e2a16cb51e8191176d96d5a9c21bf464 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 18:30:23 -0700 Subject: [PATCH 094/126] don't send more than three unanswered chats per boot -- to avoid filling up the queues in a way that doesn't reflect real use. --- xwords4/linux/cursesmain.c | 8 ++++++-- xwords4/linux/cursesmain.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index e87955aff..c3cd2eb44 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1517,8 +1517,10 @@ curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo ) #ifdef XWFEATURE_CHAT static void -curses_util_showChat( XW_UtilCtxt* XP_UNUSED(uc), const XP_UCHAR* const msg ) +curses_util_showChat( XW_UtilCtxt* uc, const XP_UCHAR* const msg ) { + CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; + globals->nChatsSent = 0; XP_LOGF( "%s: got \"%s\"", __func__, msg ); } #endif @@ -1721,7 +1723,8 @@ chatsTimerFired( gpointer data ) { CursesAppGlobals* globals = (CursesAppGlobals*)data; - if ( COMMS_RELAYSTATE_ALLCONNECTED == globals->commsRelayState ) { + if ( COMMS_RELAYSTATE_ALLCONNECTED == globals->commsRelayState + && 3 > globals->nChatsSent ) { XP_UCHAR msg[128]; struct tm* timp; struct timeval tv; @@ -1734,6 +1737,7 @@ chatsTimerFired( gpointer data ) timp->tm_hour, timp->tm_min, timp->tm_sec ); XP_LOGF( "%s: sending \"%s\"", __func__, msg ); server_sendChat( globals->cGlobals.game.server, msg ); + ++globals->nChatsSent; } return TRUE; diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index 99f771730..fa158471b 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -69,6 +69,8 @@ struct CursesAppGlobals { const struct MenuList* menuList; XP_U16 nLinesMenu; + XP_U16 nChatsSent; + union { struct { XWStreamCtxt* stream; /* how we can reach the server */ From 6cd3ebd780dc520dd37d2666251c03a1509622f9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 18:31:02 -0700 Subject: [PATCH 095/126] improve logging --- xwords4/linux/linuxmain.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index aa1f6e868..48d05800d 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -785,15 +785,14 @@ linux_init_relay_socket( CommonGlobals* cGlobals, const CommsAddrRec* addrRec ) /* make a local copy of the address to send to */ sock = socket( AF_INET, SOCK_STREAM, 0 ); if ( sock == -1 ) { - XP_DEBUGF( "socket returned -1\n" ); + XP_DEBUGF( "%s: socket returned -1\n", __func__ ); goto done; } to_sock.sin_port = htons( addrRec->u.ip_relay.port ); - XP_STATUSF( "1: sending to port %d", addrRec->u.ip_relay.port ); host = gethostbyname( addrRec->u.ip_relay.hostName ); if ( NULL == host ) { - XP_WARNF( "%s: gethostbyname(%s) returned -1", __func__, + XP_WARNF( "%s: gethostbyname(%s) failed", __func__, addrRec->u.ip_relay.hostName ); sock = -1; goto done; @@ -1054,20 +1053,20 @@ linux_close_socket( CommonGlobals* cGlobals ) } int -blocking_read( int fd, unsigned char* buf, int len ) +blocking_read( int fd, unsigned char* buf, const int len ) { int nRead = 0; while ( nRead < len ) { - XP_LOGF( "%s(fd=%d): blocking for %d bytes", __func__, fd, len ); ssize_t siz = read( fd, buf + nRead, len - nRead ); if ( siz <= 0 ) { - XP_LOGF( "read => %d, errno=%d (\"%s\")", nRead, + XP_LOGF( "%s: read => %d, errno=%d (\"%s\")", __func__, nRead, errno, strerror(errno) ); nRead = -1; break; } nRead += siz; } + XP_LOGF( "%s(fd=%d, sought=%d) => %d", __func__, fd, len, nRead ); return nRead; } From 858d00332af5d2dbfbf0d42f8ac8ddab1aea91d2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 2 Jul 2013 18:49:48 -0700 Subject: [PATCH 096/126] when an initial message from a client arrives with channel already set, drop it (after asserting in debug code.) I'm not yet sure why this happens, but giving it a new channel is not the right move. --- xwords4/common/comms.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 18825e368..a346727fd 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -956,7 +956,6 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec, XP_PlayerAddr channelNo, XWStreamCtxt* stream ) { XP_LOGF( "%s(channelNo=%x)", __func__, channelNo ); - // XP_ASSERT( 0 == (channelNo & CHANNEL_MASK) ); XP_U16 headerLen; XP_U16 streamSize = NULL == stream? 0 : stream_getSize( stream ); MsgID lastMsgSaved = (!!rec)? rec->lastMsgSaved : 0; @@ -1066,10 +1065,6 @@ addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem ) XP_ASSERT( comms->queueLen > 0 ); } ++comms->queueLen; - XP_LOGF( "%s: queueLen now %d after channelNo: %d; msgID: " XP_LD - "; len: %d", __func__, comms->queueLen, - newMsgElem->channelNo & CHANNEL_MASK, newMsgElem->msgID, - newMsgElem->len ); } /* addToQueue */ #ifdef DEBUG @@ -1081,7 +1076,7 @@ printQueue( const CommsCtxt* comms ) for ( elem = comms->msgQueueHead, ii = 0; ii < comms->queueLen; elem = elem->next, ++ii ) { - XP_STATUSF( "\t%d: channel: %x; msgID=" XP_LD + XP_LOGF( "\t%d: channel: %x; msgID=" XP_LD #ifdef COMMS_CHECKSUM "; check=%s" #endif @@ -1132,8 +1127,8 @@ freeElem( const CommsCtxt* XP_UNUSED_DBG(comms), MsgQueueElem* elem ) static void removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID ) { - XP_STATUSF( "%s: remove msgs <= " XP_LD " for channel %x (queueLen: %d)", - __func__, msgID, channelNo, comms->queueLen ); + XP_LOGF( "%s: remove msgs <= " XP_LD " for channel %x (queueLen: %d)", + __func__, msgID, channelNo, comms->queueLen ); if ( (channelNo == 0) || !!getRecordFor( comms, NULL, channelNo, XP_FALSE ) ) { @@ -1169,7 +1164,7 @@ removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID ) } } - XP_STATUSF( "%s: queueLen now %d", __func__, comms->queueLen ); + XP_LOGF( "%s: queueLen now %d", __func__, comms->queueLen ); #ifdef DEBUG assertQueueOk( comms ); @@ -1747,7 +1742,7 @@ validateInitialMessage( CommsCtxt* comms, if ( addRec ) { if ( comms->isServer ) { XP_LOGF( "%s: looking at channelNo: %x", __func__, *channelNo ); - XP_ASSERT( (*channelNo && CHANNEL_MASK) == 0 ); + XP_ASSERT( (*channelNo & CHANNEL_MASK) == 0 ); *channelNo |= ++comms->nextChannelNo; XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK ); } @@ -1769,12 +1764,22 @@ validateInitialMessage( CommsCtxt* comms, } else { if ( comms->isServer ) { XP_ASSERT( (*channelNo & CHANNEL_MASK) == 0 ); - *channelNo |= ++comms->nextChannelNo; - XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK ); + if ( 0 == (*channelNo & CHANNEL_MASK) ) { + *channelNo |= ++comms->nextChannelNo; + XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK ); + } else { + /* Why do I sometimes see these in the middle of a game + with lots of messages already sent? connID of 0 should + only happen at the start! */ + XP_LOGF( "%s: dropping msg because channel already set", + __func__ ); + goto errExit; + } } rec = rememberChannelAddress( comms, *channelNo, senderID, addr ); } } + errExit: LOG_RETURNF( XP_P, rec ); return rec; } /* validateInitialMessage */ @@ -1866,6 +1871,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream, } else if ( comms->connID == connID ) { rec = validateChannelMessage( comms, retAddr, channelNo, msgID, lastMsgRcd ); + } else { + XP_LOGF( "%s: unexpected connID; dropping message", __func__ ); } messageValid = (NULL != rec) From 3364dcbf462a2cd549e6526a8bb6bdfc946e9b8f Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 3 Jul 2013 07:26:23 -0700 Subject: [PATCH 097/126] remove unused enum --- xwords4/common/server.c | 1 - xwords4/common/states.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 8d2bec17e..78e061a2b 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -169,7 +169,6 @@ getStateStr( XW_State st ) CASESTR(XWSTATE_NONE); CASESTR(XWSTATE_BEGIN); CASESTR(XWSTATE_NEED_SHOWSCORE); - CASESTR(XWSTATE_WAITING_ALL_REG); CASESTR(XWSTATE_RECEIVED_ALL_REG); CASESTR(XWSTATE_NEEDSEND_BADWORD_INFO); CASESTR(XWSTATE_MOVE_CONFIRM_WAIT); diff --git a/xwords4/common/states.h b/xwords4/common/states.h index d7ddb13ad..fa7e048d6 100644 --- a/xwords4/common/states.h +++ b/xwords4/common/states.h @@ -25,7 +25,7 @@ enum { XWSTATE_BEGIN, __UNUSED1, /* was XWSTATE_POOL_INITED */ XWSTATE_NEED_SHOWSCORE, /* client-only */ - XWSTATE_WAITING_ALL_REG, /* includes waiting for dict from server */ + __XWSTATE_WAITING_ALL_REG, /* unused */ XWSTATE_RECEIVED_ALL_REG, /* includes waiting for dict from server */ XWSTATE_NEEDSEND_BADWORD_INFO, XWSTATE_MOVE_CONFIRM_WAIT, /* client's waiting to hear back */ From 8e12a8ef84810c99f1a13b198a30f231a4e1f9d3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 3 Jul 2013 07:36:15 -0700 Subject: [PATCH 098/126] when we get a fatal error from postgres server, try killing then recreating the connection. This is untested, as fatal errors are rare. --- xwords4/relay/dbmgr.cpp | 13 +++++++++++++ xwords4/relay/dbmgr.h | 1 + 2 files changed, 14 insertions(+) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index f1b173404..ae080075d 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -762,6 +762,7 @@ DBMgr::execSql( const char* const query ) logf( XW_LOGERROR, "%s: PQexec=>%s;%s", __func__, PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result) ); + clearThreadConn(); usleep( 20000 ); } PQclear( result ); @@ -1143,6 +1144,18 @@ destr_function( void* conn ) PQfinish( pgconn ); } +void +DBMgr::clearThreadConn() +{ + logf( XW_LOGERROR, "%s called()", __func__ ); + PGconn* conn = (PGconn*)pthread_getspecific( m_conn_key ); + if ( NULL != conn ) { + PQfinish( conn ); + int result = pthread_setspecific( m_conn_key, NULL ); + assert( 0 == result ); + } +} + PGconn* DBMgr::getThreadConn( void ) { diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 70cf50ff5..bed8a96c6 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -135,6 +135,7 @@ class DBMgr { int byteaIndex, unsigned char* buf, size_t* buflen ); PGconn* getThreadConn( void ); + void clearThreadConn(); void conn_key_alloc(); pthread_key_t m_conn_key; From 7229535be7a3816915f06fc9f3449f3bf26dfe10 Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 3 Jul 2013 07:39:05 -0700 Subject: [PATCH 099/126] insist on non-0 channel ID *net of mask*; don't send acks when we don't have a connID yet, as this confuses the server. This fixes assertions in testing that were pretty common when chat was enabled; it may also fix the reported problem on Android that games get hosed when people are using chat, as the server's response (without the assert) was to add a new channel. --- xwords4/common/comms.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index a346727fd..3f5412706 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1008,7 +1008,7 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec, XP_U16 comms_getChannelSeed( CommsCtxt* comms ) { - while ( comms->channelSeed == 0 ) { + while ( 0 == (comms->channelSeed & ~CHANNEL_MASK) ) { comms->channelSeed = XP_RANDOM(); XP_LOGF( "%s: channelSeed: %.4X", __func__, comms->channelSeed ); } @@ -1297,25 +1297,27 @@ comms_resendAll( CommsCtxt* comms, XP_Bool force ) void comms_ackAny( CommsCtxt* comms ) { + if ( CONN_ID_NONE == comms->connID ) { + XP_LOGF( "%s: doing nothing because connID still unset", __func__ ); + } else { #ifdef DEBUG - XP_Bool noneSent = XP_TRUE; + XP_U16 nSent = 0; #endif - AddressRecord* rec; - for ( rec = comms->recs; !!rec; rec = rec->next ) { - if ( rec->lastMsgAckd < rec->lastMsgRcd ) { + AddressRecord* rec; + for ( rec = comms->recs; !!rec; rec = rec->next ) { + if ( rec->lastMsgAckd < rec->lastMsgRcd ) { #ifdef DEBUG - noneSent = XP_FALSE; + ++nSent; #endif - XP_LOGF( "%s: channel %x; %ld < %ld: rec needs ack", __func__, - rec->channelNo, rec->lastMsgAckd, rec->lastMsgRcd ); - sendEmptyMsg( comms, rec ); + XP_LOGF( "%s: channel %x; %ld < %ld: rec needs ack", __func__, + rec->channelNo, rec->lastMsgAckd, rec->lastMsgRcd ); + sendEmptyMsg( comms, rec ); + } } - } #ifdef DEBUG - if ( noneSent ) { - XP_LOGF( "%s: nothing to send", __func__ ); - } + XP_LOGF( "%s: sent for %d channels", __func__, nSent ); #endif + } } #endif From e6248210a5e70f06174e5158e9ecdfd954ab13d3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 4 Jul 2013 12:37:10 -0700 Subject: [PATCH 100/126] when formatting final scores, print all players with top score as Winner (fixing bug that occurred in case of a tie) --- xwords4/common/server.c | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 78e061a2b..400f05977 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -3041,10 +3041,9 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) { ScoresArray scores; ScoresArray tilePenalties; - XP_U16 place, nPlayers; + XP_U16 place; XP_S16 quitter = server->nv.quitter; XP_Bool quitterDone = XP_FALSE; - XP_USE(quitter); ModelCtxt* model = server->vol.model; const XP_UCHAR* addString = util_getUserString( server->vol.util, STRD_REMAINING_TILES_ADD ); @@ -3052,18 +3051,18 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) STRD_UNUSED_TILES_SUB ); XP_UCHAR* timeStr; CurGameInfo* gi = server->vol.gi; + const XP_U16 nPlayers = gi->nPlayers; XP_ASSERT( server->nv.gameState == XWSTATE_GAMEOVER ); model_figureFinalScores( model, &scores, &tilePenalties ); - nPlayers = gi->nPlayers; - + XP_S16 winningScore = IMPOSSIBLY_LOW_SCORE; for ( place = 1; !quitterDone; ++place ) { XP_UCHAR timeBuf[16]; XP_UCHAR buf[128]; - XP_S16 highestScore = IMPOSSIBLY_LOW_SCORE; - XP_S16 highestIndex = -1; + XP_S16 thisScore = IMPOSSIBLY_LOW_SCORE; + XP_S16 thisIndex = -1; const XP_UCHAR* placeStr = NULL; XP_UCHAR placeBuf[32]; XP_UCHAR tmpbuf[48]; @@ -3072,22 +3071,27 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) /* Find the next player we should print */ for ( ii = 0; ii < nPlayers; ++ii ) { - if ( quitter != ii && scores.arr[ii] > highestScore ) { - highestIndex = ii; - highestScore = scores.arr[ii]; + if ( quitter != ii && scores.arr[ii] > thisScore ) { + thisIndex = ii; + thisScore = scores.arr[ii]; } } - if ( highestIndex == -1 ) { + /* save top score overall to test for winner, including tie case */ + if ( 1 == place ) { + winningScore = thisScore; + } + + if ( thisIndex == -1 ) { if ( quitter >= 0 ) { XP_ASSERT( !quitterDone ); - highestIndex = quitter; + thisIndex = quitter; quitterDone = XP_TRUE; placeKey = STR_RESIGNED; } else { break; /* we're done */ } - } else if ( place == 1 ) { + } else if ( thisScore == winningScore ) { placeKey = STR_WINNER; } @@ -3102,7 +3106,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) timeStr = (XP_UCHAR*)""; if ( gi->timerEnabled ) { - XP_U16 penalty = player_timePenalty( gi, highestIndex ); + XP_U16 penalty = player_timePenalty( gi, thisIndex ); if ( penalty > 0 ) { XP_SNPRINTF( timeBuf, sizeof(timeBuf), util_getUserString( @@ -3113,18 +3117,18 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) } } - firstDone = model_getNumTilesTotal( model, highestIndex) == 0; + firstDone = model_getNumTilesTotal( model, thisIndex) == 0; XP_SNPRINTF( tmpbuf, sizeof(tmpbuf), (firstDone? addString:subString), firstDone? - tilePenalties.arr[highestIndex]: - -tilePenalties.arr[highestIndex] ); + tilePenalties.arr[thisIndex]: + -tilePenalties.arr[thisIndex] ); XP_SNPRINTF( buf, sizeof(buf), (XP_UCHAR*)"[%s] %s: %d" XP_CR " (%d %s%s)", placeStr, - emptyStringIfNull(gi->players[highestIndex].name), - scores.arr[highestIndex], - model_getPlayerScore( model, highestIndex ), + emptyStringIfNull(gi->players[thisIndex].name), + scores.arr[thisIndex], + model_getPlayerScore( model, thisIndex ), tmpbuf, timeStr ); if ( 1 < place ) { @@ -3133,7 +3137,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream ) stream_catString( stream, buf ); /* Don't consider this one next time around */ - scores.arr[highestIndex] = IMPOSSIBLY_LOW_SCORE; + scores.arr[thisIndex] = IMPOSSIBLY_LOW_SCORE; } } /* server_writeFinalScores */ From 4b5af7ebdffa8d65b1e1679dada4126bd030a6ad Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 4 Jul 2013 12:37:55 -0700 Subject: [PATCH 101/126] don't try to read from socket after it's been closed --- xwords4/linux/linuxmain.c | 61 ++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 48d05800d..6be58b0da 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -1055,6 +1055,7 @@ linux_close_socket( CommonGlobals* cGlobals ) int blocking_read( int fd, unsigned char* buf, const int len ) { + assert( -1 != fd ); int nRead = 0; while ( nRead < len ) { ssize_t siz = read( fd, buf + nRead, len - nRead ); @@ -1073,47 +1074,49 @@ blocking_read( int fd, unsigned char* buf, const int len ) int linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize ) { + ssize_t nRead = -1; int sock = cGlobals->socket; - unsigned short tmp; - ssize_t nRead = blocking_read( sock, (unsigned char*)&tmp, sizeof(tmp) ); - if ( nRead != 2 ) { - nRead = -1; - } else { - unsigned short packetSize = ntohs( tmp ); - if ( 0 == packetSize || bufSize <= packetSize ) { + if ( -1 != sock ) { + unsigned short tmp; + nRead = blocking_read( sock, (unsigned char*)&tmp, sizeof(tmp) ); + if ( nRead != 2 ) { nRead = -1; } else { - nRead = blocking_read( sock, buf, packetSize ); - if ( nRead != packetSize ) { + unsigned short packetSize = ntohs( tmp ); + if ( 0 == packetSize || bufSize <= packetSize ) { nRead = -1; } else { - LaunchParams* params = cGlobals->params; - ++params->nPacketsRcvd; - if ( params->dropNthRcvd == 0 ) { - /* do nothing */ - } else if ( params->dropNthRcvd > 0 ) { - if ( params->nPacketsRcvd == params->dropNthRcvd ) { - XP_LOGF( "%s: dropping %dth packet per --drop-nth-packet", - __func__, params->nPacketsRcvd ); - nRead = -1; - } + nRead = blocking_read( sock, buf, packetSize ); + if ( nRead != packetSize ) { + nRead = -1; } else { - if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) { - XP_LOGF( "%s: RANDOMLY dropping %dth packet " - "per --drop-nth-packet", - __func__, params->nPacketsRcvd ); - nRead = -1; + LaunchParams* params = cGlobals->params; + ++params->nPacketsRcvd; + if ( params->dropNthRcvd == 0 ) { + /* do nothing */ + } else if ( params->dropNthRcvd > 0 ) { + if ( params->nPacketsRcvd == params->dropNthRcvd ) { + XP_LOGF( "%s: dropping %dth packet per --drop-nth-packet", + __func__, params->nPacketsRcvd ); + nRead = -1; + } + } else { + if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) { + XP_LOGF( "%s: RANDOMLY dropping %dth packet " + "per --drop-nth-packet", + __func__, params->nPacketsRcvd ); + nRead = -1; + } } } } } - } - if ( -1 == nRead ) { - linux_close_socket( cGlobals ); - comms_transportFailed( cGlobals->game.comms ); + if ( -1 == nRead ) { + linux_close_socket( cGlobals ); + comms_transportFailed( cGlobals->game.comms ); + } } - XP_LOGF( "%s=>%d", __func__, nRead ); return nRead; } /* linux_relay_receive */ From cd1886a751c3de38d1d1e12ad103fc3e19ce5183 Mon Sep 17 00:00:00 2001 From: Eric House Date: Thu, 4 Jul 2013 20:46:59 -0700 Subject: [PATCH 102/126] changes from translator --- .../android/XWords4/res/values-pt/strings.xml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/xwords4/android/XWords4/res/values-pt/strings.xml b/xwords4/android/XWords4/res/values-pt/strings.xml index 41293c675..fdb56367a 100644 --- a/xwords4/android/XWords4/res/values-pt/strings.xml +++ b/xwords4/android/XWords4/res/values-pt/strings.xml @@ -15,19 +15,19 @@ --> - Use o botão abaixo para criar um - jogo. Toque num jogo existente para continuá-lo ou dê um toque + Use o botão abaixo para criar um novo + jogo. Selecione um jogo existente para continuá-lo ou dê um toque longo para outras opções. Você pode esconder essa mensagem - e os botões abaixo na seção Aparência das Configurações (acessadas + e os botões abaixo na seção Aparência das Configurações (acessada através do botão menu do seu dispositivo.) - Adicionar jogo - Adicionar grupo + Novo jogo + Novo grupo @@ -114,13 +114,13 @@ Checar jogadas - Checando retransmissor por jogadas + Procurando jogadas no servidor etc... Nenhum jogo encontrado que conecta - pelo retransmissor. + pelo servidor. Excluir todos @@ -166,7 +166,7 @@ Jogos que já se conectaram ao - retransmissor não podem ser copiados. Use \"Novo a partir de\" + servidor não podem ser copiados. Use \"Novo a partir de\" para uma cópia pronta para jogar com todas as mesmas configurações. @@ -412,13 +412,13 @@ - Ignorar falsas + Ignorar palavras falsas - Avisar se for falsa + Avisar se a palavra for falsa - Não permitir falsas + Não permitir palavras falsas @@ -588,7 +588,7 @@ registered with the relay in this game. This should be seen only once per game. --> Dispositivo %1$d conectado ao - retransmissor na sala \"%2$s\". Esperando por %3$d jogador(es). + servidor na sala \"%2$s\". Esperando por %3$d jogador(es). - O retransmissor perdeu contato com + O servidor perdeu contato com outro dispositivo nesse jogos. - Essa ação procura no retransmissor + Essa ação procura no servidor por jogadas/mensagens pendentes para todos os jogos de rede e marca aqueles com jogadas pendentes. Quando você abrir um jogo marcado, ele fará a conexão e sincronização. (Numa versão futura @@ -1729,7 +1729,7 @@ in the game but not the last either. So it will only occur for games with more than two devices, which are rare. --> Você conectou e entrou num - jogo no retransmissor. Você será notificado quando o(s) + jogo no servidor. Você será notificado quando o(s) dispositivo(s) restantes(s) tiver(em) entrado na sua sala e o jogo puder começar. @@ -1738,7 +1738,7 @@ game to do so, i.e. the game is now complete and you should expect play to begin. --> Você conectou e entrou num jogo - no retransmissor, a sala agora está cheia. O dispositivo que criou + no servidor, a sala agora está cheia. O dispositivo que criou a sala fará a distribuição inicial de pedras e o jogo pode começar. @@ -1853,7 +1853,7 @@ Procurar %s em - Pular + Pular vez Esse botão permite que você @@ -2073,7 +2073,7 @@ Nenhuma mensagem foi recebida. - internet/retransmissor + internet/servidor sms/texto From 7419e3506a15a4441406d402c0a14a4e51bd9d89 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 06:19:28 -0700 Subject: [PATCH 103/126] add missing string to translation --- xwords4/android/XWords4/res/values-pt/strings.xml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/res/values-pt/strings.xml b/xwords4/android/XWords4/res/values-pt/strings.xml index fdb56367a..16093d058 100644 --- a/xwords4/android/XWords4/res/values-pt/strings.xml +++ b/xwords4/android/XWords4/res/values-pt/strings.xml @@ -1886,8 +1886,11 @@ Nenhuma palavra em %1$s começa com %2$s. - Esse botão abre o navegador de - listas de palavras na lista do jogador atual. + Esse botão abre o seletor de listas + de palavras na lista do jogador atual. + Esse botão abre o seletor de listas + de palavras na lista de sua escolha. + A lista de palavras %s só contém informações de pedras. Não há palavras para mostrar. @@ -2197,4 +2200,7 @@ Mesmo se puderem ser mais altas Mover jogo %s + + Listas de palavras + From 883cec06523dd55d83af9d73282fcbadaf89c0b6 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 06:56:43 -0700 Subject: [PATCH 104/126] log whether b64 encoding is being used --- xwords4/relay/dbmgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index ae080075d..739a63fca 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -65,6 +65,7 @@ DBMgr::DBMgr() int tmp; RelayConfigs::GetConfigs()->GetValueFor( "USE_B64", &tmp ); m_useB64 = tmp != 0; + logf( XW_LOGERROR, "%s: m_useB64=%d", __func__, m_useB64 ); pthread_key_create( &m_conn_key, destr_function ); @@ -1099,7 +1100,6 @@ DBMgr::getCountWhere( const char* table, string& test ) assert( 1 == PQntuples( result ) ); int count = atoi( PQgetvalue( result, 0, 0 ) ); PQclear( result ); - logf( XW_LOGINFO, "%s(%s)=>%d", __func__, query.c_str(), count ); return count; } From a93eb4a51104c764a10592c87d8c6550e2346351 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 08:01:31 -0700 Subject: [PATCH 105/126] fix assertion failure: log and drop packet when hid outside of accepted range. --- xwords4/relay/xwrelay.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index d6572102c..b7993707d 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -203,13 +203,24 @@ parseRelayID( const unsigned char** const inp, const unsigned char* const end, if ( ok ) { strncpy( buf, (char*)*inp, connNameLen ); buf[connNameLen] = '\0'; - *hid = atoi( hidp+1 ); char* endptr; *hid = strtol( hidp + 1, &endptr, 10 ); - if ( '\n' == *endptr ) { - ++endptr; - } - *inp = (unsigned char*)endptr; + + if ( *hid >= 0 && *hid <= 4 ) { + if ( '\n' == *endptr ) { + ++endptr; + } + *inp = (unsigned char*)endptr; + } else { + ok = false; + + int len = end - *inp; + char buf[len+1]; + memcpy( buf, *inp, len); + buf[len] = '\0'; + logf( XW_LOGERROR, "%s: got bad hid %d from str \"%s\"", __func__, + *hid, buf ); + } } if ( !ok ) { logf( XW_LOGERROR, "%s failed", __func__ ); From 23c66e93ab4e6fe3325a4dea793448b9c6e9c3a9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 08:02:22 -0700 Subject: [PATCH 106/126] add flag required to compile on some linux systems --- xwords4/relay/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile index d465f3b65..06d8a8c73 100644 --- a/xwords4/relay/Makefile +++ b/xwords4/relay/Makefile @@ -45,6 +45,7 @@ OBJ = $(patsubst %.cpp,%.o,$(SRC)) LDFLAGS += -pthread -g $(STATIC) LDFLAGS += -L$(shell pg_config --libdir) LDFLAGS += $(shell pkg-config --libs glib-2.0) +LDFLAGS += -lrt CPPFLAGS += -DSPAWN_SELF -g -Wall CPPFLAGS += -I $(shell pg_config --includedir) From 2cd84c4229f5277aaea46090382cd6dc9fc46240 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 08:03:03 -0700 Subject: [PATCH 107/126] test that address is good each time through loop --- xwords4/relay/cref.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/xwords4/relay/cref.cpp b/xwords4/relay/cref.cpp index c5fd9805e..106d5f57c 100644 --- a/xwords4/relay/cref.cpp +++ b/xwords4/relay/cref.cpp @@ -857,21 +857,19 @@ void CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr ) { logf( XW_LOGVERBOSE0, "%s(dest=%d)", __func__, dest ); - assert( dest > 0 && dest <= 4 ); - if ( addr->isCurrent() ) { - DBMgr* dbmgr = DBMgr::Get(); - const char* cname = ConnName(); - for ( ; ; ) { - unsigned char buf[MAX_MSG_LEN]; - size_t buflen = sizeof(buf); - int msgID; - if ( !dbmgr->GetStoredMessage( cname, dest, buf, &buflen, &msgID ) - || ! send_with_length( addr, dest, buf, buflen, true ) ) { - break; - } - dbmgr->RemoveStoredMessages( &msgID, 1 ); - } + + DBMgr* dbmgr = DBMgr::Get(); + const char* cname = ConnName(); + while ( addr->isCurrent() ) { + unsigned char buf[MAX_MSG_LEN]; + size_t buflen = sizeof(buf); + int msgID; + if ( !dbmgr->GetStoredMessage( cname, dest, buf, &buflen, &msgID ) + || ! send_with_length( addr, dest, buf, buflen, true ) ) { + break; + } + dbmgr->RemoveStoredMessages( &msgID, 1 ); } } /* send_stored_messages */ From 254b82104c7fb6179524340c61db8526003bd306 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 08:03:31 -0700 Subject: [PATCH 108/126] reduce priority of log message --- xwords4/relay/dbmgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 739a63fca..2eafc4517 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -65,7 +65,7 @@ DBMgr::DBMgr() int tmp; RelayConfigs::GetConfigs()->GetValueFor( "USE_B64", &tmp ); m_useB64 = tmp != 0; - logf( XW_LOGERROR, "%s: m_useB64=%d", __func__, m_useB64 ); + logf( XW_LOGINFO, "%s: m_useB64=%d", __func__, m_useB64 ); pthread_key_create( &m_conn_key, destr_function ); From 713551fa43af61612d42c56645d562e2beeff442 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:23:54 -0700 Subject: [PATCH 109/126] only log recv() errors that aren't related to non-blocking sockets --- xwords4/relay/udpqueue.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xwords4/relay/udpqueue.cpp b/xwords4/relay/udpqueue.cpp index 488521a6f..83802c6b0 100644 --- a/xwords4/relay/udpqueue.cpp +++ b/xwords4/relay/udpqueue.cpp @@ -53,8 +53,10 @@ PartialPacket::readAtMost( int len ) ssize_t nRead = recv( m_sock, tmp, len, 0 ); if ( 0 > nRead ) { // error case m_errno = errno; - logf( XW_LOGERROR, "%s(len=%d, socket=%d): recv failed: %d (%s)", __func__, - len, m_sock, m_errno, strerror(m_errno) ); + if ( !stillGood() ) { + logf( XW_LOGERROR, "%s(len=%d, socket=%d): recv failed: %d (%s)", __func__, + len, m_sock, m_errno, strerror(m_errno) ); + } } else if ( 0 == nRead ) { // remote socket closed logf( XW_LOGINFO, "%s: remote closed (socket=%d)", __func__, m_sock ); m_errno = -1; // so stillGood will fail From e8022d44ead2bac08e913c37d11bd0f2c11f2731 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:26:17 -0700 Subject: [PATCH 110/126] correctly parse hid from relayID, fixing assertion failure. I'm not sure how it ever worked.... --- xwords4/relay/xwrelay.cpp | 53 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index b7993707d..395bdcd2d 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -203,27 +203,30 @@ parseRelayID( const unsigned char** const inp, const unsigned char* const end, if ( ok ) { strncpy( buf, (char*)*inp, connNameLen ); buf[connNameLen] = '\0'; - char* endptr; - *hid = strtol( hidp + 1, &endptr, 10 ); - if ( *hid >= 0 && *hid <= 4 ) { - if ( '\n' == *endptr ) { - ++endptr; - } - *inp = (unsigned char*)endptr; - } else { - ok = false; + ++hidp; // skip '/' + *hid = *hidp - '0'; // assume it's one byte, as should be in range '0'--'4' + // logf( XW_LOGERROR, "%s: read hid of %d from %s", __func__, *hid, hidp ); - int len = end - *inp; - char buf[len+1]; - memcpy( buf, *inp, len); - buf[len] = '\0'; - logf( XW_LOGERROR, "%s: got bad hid %d from str \"%s\"", __func__, - *hid, buf ); - } + if ( *hid >= 0 && *hid <= 4 ) { + const char* endptr = hidp + 1; + if ( '\n' == *endptr ) { + ++endptr; + } + *inp = (unsigned char*)endptr; + } else { + ok = false; + + int len = end - *inp; + char buf[len+1]; + memcpy( buf, *inp, len); + buf[len] = '\0'; + logf( XW_LOGERROR, "%s: got bad hid %d from str \"%s\"", __func__, + *hid, buf ); + } } if ( !ok ) { - logf( XW_LOGERROR, "%s failed", __func__ ); + logf( XW_LOGERROR, "%s failed", __func__ ); } return ok; } @@ -1050,11 +1053,11 @@ handlePutMessage( SafeCref& scr, HostID hid, const AddrInfo* addr, if ( getNetByte( bufp, end, &cmd ) && getNetByte( bufp, end, &src ) && getNetByte( bufp, end, &dest ) ) { - success = true; // meaning, buffer content looks ok + success = true; // meaning, buffer content looks ok *bufp = start + len; - if ( ( cmd == XWRELAY_MSG_TORELAY_NOCONN ) && ( hid == dest ) ) { - scr.PutMsg( src, addr, dest, start, len ); - } + if ( ( cmd == XWRELAY_MSG_TORELAY_NOCONN ) && ( hid == dest ) ) { + scr.PutMsg( src, addr, dest, start, len ); + } } logf( XW_LOGINFO, "%s()=>%d", __func__, success ); return success; @@ -1100,9 +1103,9 @@ handleProxyMsgs( int sock, const AddrInfo* addr, const unsigned char* bufp, } } } - if ( end - bufp != 1 ) { - logf( XW_LOGERROR, "%s: buf != end: %p vs %p", __func__, bufp, end ); - } + if ( end - bufp != 1 ) { + logf( XW_LOGERROR, "%s: buf != end: %p vs %p (+1)", __func__, bufp, end ); + } // assert( bufp == end ); // don't ship with this!!! } } // handleProxyMsgs @@ -1186,7 +1189,7 @@ proxy_thread_proc( UdpThreadClosure* utc ) int olen = 0; /* return a 0-length message */ write( socket, &olen, sizeof(olen) ); break; /* PRX_DEVICE_GONE */ - } + } default: logf( XW_LOGERROR, "unexpected command %d", __func__, cmd ); break; From f87ffe7a968e11c6391ee5f2e156b6c4422c358c Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:28:27 -0700 Subject: [PATCH 111/126] display new column --- xwords4/relay/scripts/showgames.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/relay/scripts/showgames.php b/xwords4/relay/scripts/showgames.php index 0c0e09999..71a9dd3ba 100644 --- a/xwords4/relay/scripts/showgames.php +++ b/xwords4/relay/scripts/showgames.php @@ -99,7 +99,7 @@ $cols = array( new Column("dead", "D", "capitalize", false ), new Column("nperdevice", "NP", "identity", true ), new Column("ack", "A", "identity", true ), new Column("devids", "DevIDs", "identity", true ), - new Column("nsent", "Sent", "identity", false ), + new Column("nsents", "Sent", "identity", true ), new Column("addrs", "Dev. addr", "ip_to_host", true ), new Column("ctime", "Created", "print_date", false ), new Column("mtimes", "Last contact", "print_date", true ), From 232b27e06a68e2cbdf939178675af5f4b1fee580 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:31:39 -0700 Subject: [PATCH 112/126] combine two log lines --- xwords4/relay/tpool.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index 8cd246488..f15502594 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -156,16 +156,15 @@ XWThreadPool::RemoveSocket( const AddrInfo* addr ) { RWWriteLock ml( &m_activeSocketsRWLock ); - logf( XW_LOGINFO, "%s: START: %d sockets active", __func__, - m_activeSockets.size() ); + size_t prevSize = m_activeSockets.size(); map::iterator iter = m_activeSockets.find( addr->socket() ); if ( m_activeSockets.end() != iter && iter->second.m_addr.equals( *addr ) ) { m_activeSockets.erase( iter ); found = true; } - logf( XW_LOGINFO, "%s: AFTER: %d sockets active", __func__, - m_activeSockets.size() ); + logf( XW_LOGINFO, "%s: AFTER: %d sockets active (was %d)", __func__, + m_activeSockets.size(), prevSize ); } return found; } /* RemoveSocket */ @@ -173,7 +172,6 @@ XWThreadPool::RemoveSocket( const AddrInfo* addr ) void XWThreadPool::CloseSocket( const AddrInfo* addr ) { -/* bool do_interrupt = false; */ assert( addr->isTCP() ); if ( !RemoveSocket( addr ) ) { MutexLock ml( &m_queueMutex ); @@ -181,7 +179,6 @@ XWThreadPool::CloseSocket( const AddrInfo* addr ) while ( iter != m_queue.end() ) { if ( iter->m_info.m_addr.equals( *addr ) ) { m_queue.erase( iter ); -/* do_interrupt = true; */ break; } ++iter; @@ -189,13 +186,12 @@ XWThreadPool::CloseSocket( const AddrInfo* addr ) } logf( XW_LOGINFO, "CLOSING socket %d", addr->socket() ); close( addr->socket() ); -/* if ( do_interrupt ) { */ + /* We always need to interrupt the poll because the socket we're closing will be in the list being listened to. That or we need to drop sockets that have been removed on some other thread while the poll call's blocking.*/ interrupt_poll(); -/* } */ } void From bbac923f6894baa3b3ee998648f6f3c765695d26 Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:32:41 -0700 Subject: [PATCH 113/126] assert queueLen at the point where it'll be easier track why it's overgrowing --- xwords4/common/comms.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 3f5412706..4835842c2 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1065,6 +1065,8 @@ addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem ) XP_ASSERT( comms->queueLen > 0 ); } ++comms->queueLen; + XP_ASSERT( comms->queueLen <= 128 ); /* reasonable limit in testing */ + printQueue( comms ); } /* addToQueue */ #ifdef DEBUG From fb32c847ae11f6bd1efaa0f4d2e5c57ddb8a545a Mon Sep 17 00:00:00 2001 From: Eric House Date: Fri, 5 Jul 2013 21:37:38 -0700 Subject: [PATCH 114/126] more changes from translator --- .../android/XWords4/res/values-pt/strings.xml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/xwords4/android/XWords4/res/values-pt/strings.xml b/xwords4/android/XWords4/res/values-pt/strings.xml index 16093d058..1ecf9e755 100644 --- a/xwords4/android/XWords4/res/values-pt/strings.xml +++ b/xwords4/android/XWords4/res/values-pt/strings.xml @@ -536,16 +536,16 @@ - 2L + L2 - 2P + P2 - 3L + L3 - 3P + P3 @@ -971,10 +971,10 @@ ############################################################ --> - Cores individuais + Cores Editar as cores usadas - no tabuleiro + no jogo - Cor das linhas + Cor das linhas de posição Fundo das pedras @@ -1103,7 +1103,7 @@ is distracting, presumably because they're using tablets with large enough screens that they always know where they're tapping. --> - Desligar mira + Desligar linhas de posição Não indicar visualmente a casa do tabuleiro selecionada @@ -1886,10 +1886,10 @@ Nenhuma palavra em %1$s começa com %2$s. - Esse botão abre o seletor de listas - de palavras na lista do jogador atual. + Esse botão abre o navegador de + listas de palavras na lista do jogador atual. Esse botão abre o seletor de listas - de palavras na lista de sua escolha. + de palavras na lista de sua escolha. A lista de palavras %s só contém @@ -2055,7 +2055,7 @@ Carregando resumo do jogo... - Este é um jogo independente. Não + Este é um jogo local. Não há informações de rede. Informações de rede para jogo conectado @@ -2127,7 +2127,7 @@ Desistiu - Ganhador + Vencedor Você e o hospedeiro From 2c5ac1c293b2d8f73f221f7b4b4a1672f1a88da9 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 13:07:02 -0700 Subject: [PATCH 115/126] remove excess logging --- .../org/eehouse/android/xw4/DlgDelegate.java | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java index 83df2e390..01834e8dd 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java @@ -102,7 +102,7 @@ public class DlgDelegate { public Dialog onCreateDialog( int id ) { - DbgUtils.logf("onCreateDialog(id=%d)", id ); + // DbgUtils.logf("onCreateDialog(id=%d)", id ); Dialog dialog = null; DlgState state = findForID( id ); switch( id ) { @@ -138,11 +138,6 @@ public class DlgDelegate { // Assert.assertNull( m_dlgStates ); DlgState state = new DlgState( DIALOG_OKONLY, msg, callbackID ); addState( state ); - // m_msg = msg; - // if ( 0 != callbackID ) { - // Assert.assertTrue( 0 == m_cbckID ); - // m_cbckID = callbackID; - // } m_activity.showDialog( DIALOG_OKONLY ); } @@ -192,18 +187,10 @@ public class DlgDelegate { public void showConfirmThen( String msg, int posButton, int callbackID ) { - // FIX ME!! Need to store data per message rather than have - // assertions failing or messages dropped. - if ( false /*0 != m_cbckID*/ ) { - // DbgUtils.logf( "showConfirmThen: busy with another message; " - // + "dropping \"%s\" in favor of \"%s\"", - // msg, m_msg ); - } else { - DlgState state = new DlgState( CONFIRM_THEN, msg, posButton, - callbackID, 0 ); - addState( state ); - m_activity.showDialog( CONFIRM_THEN ); - } + DlgState state = new DlgState( CONFIRM_THEN, msg, posButton, + callbackID, 0 ); + addState( state ); + m_activity.showDialog( CONFIRM_THEN ); } public void showEmailOrSMSThen( final int callbackID ) @@ -449,7 +436,7 @@ public class DlgDelegate { private DlgState findForID( int id ) { DlgState state = m_dlgStates.get( id ); - DbgUtils.logf( "findForID(%d)=>%H", id, state ); + // DbgUtils.logf( "findForID(%d)=>%H", id, state ); return state; } @@ -459,15 +446,15 @@ public class DlgDelegate { Assert.assertNotNull( state ); // Assert.assertTrue( state == m_dlgStates.get( state.m_id ) ); m_dlgStates.remove( state.m_id ); - DbgUtils.logf( "dropState: active dialogs now %d from %d ", - m_dlgStates.size(), nDlgs ); + // DbgUtils.logf( "dropState: active dialogs now %d from %d ", + // m_dlgStates.size(), nDlgs ); } private void addState( DlgState state ) { m_dlgStates.put( state.m_id, state ); - DbgUtils.logf( "addState: there are now %d active dialogs", - m_dlgStates.size() ); + // DbgUtils.logf( "addState: there are now %d active dialogs", + // m_dlgStates.size() ); } } From 0f104db88c3c6619314771996648d926eb111019 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 13:07:12 -0700 Subject: [PATCH 116/126] list another change --- xwords4/android/XWords4/res/raw/changes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index be7dbb189..3170faef6 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -17,6 +17,9 @@
  • Dim pending score counter (at right end of tray) when it's not that player's turn or game's over
  • +
  • Move more frequently used game buttons to left where they're + more visible
  • +
  • Fix a few bugs
  • From 923d41e0eed185a12bb20f39e328cdedf32db2d3 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 13:17:26 -0700 Subject: [PATCH 117/126] fix to compile when DEBUG not set --- xwords4/common/comms.c | 2 ++ xwords4/linux/cursesmain.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 4835842c2..04c32f9ba 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -1066,7 +1066,9 @@ addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem ) } ++comms->queueLen; XP_ASSERT( comms->queueLen <= 128 ); /* reasonable limit in testing */ +#ifdef DEBUG printQueue( comms ); +#endif } /* addToQueue */ #ifdef DEBUG diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index c3cd2eb44..84f73b0ec 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1517,7 +1517,8 @@ curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo ) #ifdef XWFEATURE_CHAT static void -curses_util_showChat( XW_UtilCtxt* uc, const XP_UCHAR* const msg ) +curses_util_showChat( XW_UtilCtxt* uc, + const XP_UCHAR* const XP_UNUSED_DBG(msg) ) { CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; globals->nChatsSent = 0; From e9615cb15493c62c3791ad20143bad9c22af8cbb Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 13:31:49 -0700 Subject: [PATCH 118/126] pass additional param into java world so don't show toast every time connect to relay --- xwords4/android/XWords4/jni/xportwrapper.c | 4 ++-- .../org/eehouse/android/xw4/BoardActivity.java | 17 ++++++++++------- .../org/eehouse/android/xw4/CommsTransport.java | 7 ++++--- .../org/eehouse/android/xw4/MultiMsgSink.java | 4 ++-- .../eehouse/android/xw4/jni/TransportProcs.java | 6 ++++-- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/xwords4/android/XWords4/jni/xportwrapper.c b/xwords4/android/XWords4/jni/xportwrapper.c index d18f04492..4a089f407 100644 --- a/xwords4/android/XWords4/jni/xportwrapper.c +++ b/xwords4/android/XWords4/jni/xportwrapper.c @@ -112,12 +112,12 @@ and_xport_relayConnd( void* closure, XP_UCHAR* const room, XP_Bool reconnect, AndTransportProcs* aprocs = (AndTransportProcs*)closure; if ( NULL != aprocs->jxport ) { JNIEnv* env = *aprocs->envp; - const char* sig = "(Ljava/lang/String;IZI)V"; + const char* sig = "(Ljava/lang/String;ZIZI)V"; jmethodID mid = getMethodID( env, aprocs->jxport, "relayConnd", sig ); jstring str = (*env)->NewStringUTF( env, room ); (*env)->CallVoidMethod( env, aprocs->jxport, mid, - str, devOrder, allHere, nMissing ); + str, reconnect, devOrder, allHere, nMissing ); deleteLocalRef( env, str ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index eadcc7733..e0e8775bb 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -1030,14 +1030,17 @@ public class BoardActivity extends XWActivity // TransportProcs.TPMsgHandler interface ////////////////////////////////////////////////// - public void tpmRelayConnd( final String room, final int devOrder, - final boolean allHere, final int nMissing ) + public void tpmRelayConnd( final String room, boolean isReconnect, + final int devOrder, final boolean allHere, + final int nMissing ) { - post( new Runnable() { - public void run() { - handleConndMessage( room, devOrder, allHere, nMissing ); - } - } ); + if ( !isReconnect ) { + post( new Runnable() { + public void run() { + handleConndMessage( room, devOrder, allHere, nMissing ); + } + } ); + } } public void tpmRelayErrorProc( TransportProcs.XWRELAY_ERROR relayErr ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java index ba89de474..c50a836f1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java @@ -435,10 +435,11 @@ public class CommsTransport implements TransportProcs, } } - public void relayConnd( String room, int devOrder, boolean allHere, - int nMissing ) + public void relayConnd( String room, boolean isReconnect, int devOrder, + boolean allHere, int nMissing ) { - m_tpHandler.tpmRelayConnd( room, devOrder, allHere, nMissing ); + m_tpHandler.tpmRelayConnd( room, isReconnect, devOrder, allHere, + nMissing ); } public void relayErrorProc( XWRELAY_ERROR relayErr ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java index 9041220bc..a4dff8698 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java @@ -48,8 +48,8 @@ public class MultiMsgSink implements TransportProcs { { } - public void relayConnd( String room, int devOrder, boolean allHere, - int nMissing ) + public void relayConnd( String room, boolean isReconnect, int devOrder, + boolean allHere, int nMissing ) { } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java index 013750586..b17c29fc3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java @@ -37,7 +37,8 @@ public interface TransportProcs { }; void relayStatus( CommsRelayState newState ); - void relayConnd( String room, int devOrder, boolean allHere, int nMissing ); + void relayConnd( String room, boolean isReconnect, int devOrder, + boolean allHere, int nMissing ); public static enum XWRELAY_ERROR { NONE ,OLDFLAGS @@ -61,7 +62,8 @@ public interface TransportProcs { boolean relayNoConnProc( byte[] buf, String relayID ); public interface TPMsgHandler { - public void tpmRelayConnd( String room, int devOrder, boolean allHere, + public void tpmRelayConnd( String room, boolean isReconnect, + int devOrder, boolean allHere, int nMissing ); public void tpmRelayErrorProc( XWRELAY_ERROR relayErr ); } From a3de759d9e49a661132bf1932be7c4a3dc7cf5cd Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 19:40:07 -0700 Subject: [PATCH 119/126] fix to compile on Android when DEBUG set --- xwords4/common/comms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 04c32f9ba..5fb175a2d 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -197,6 +197,7 @@ static void putDevID( const CommsCtxt* comms, XWStreamCtxt* stream ); # endif # ifdef DEBUG static const char* relayCmdToStr( XWRELAY_Cmd cmd ); +static void printQueue( const CommsCtxt* comms ); # endif #endif #if defined RELAY_HEARTBEAT || defined COMMS_HEARTBEAT From bb68b254299bd80094e5757cca0769578fc5dc8f Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 7 Jul 2013 19:53:12 -0700 Subject: [PATCH 120/126] remove suggestion I don't have permission to use icons! --- xwords4/android/XWords4/res/values/strings.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index be763ffcb..4e5adf768 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -1797,9 +1797,7 @@ - Toolbar icons by Sarah Chu; other - credits pending permission. - + Toolbar icons by Sarah Chu. From 008cce8f372beb10a1e3a446aeb0e9f169fb514d Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 8 Jul 2013 06:30:26 -0700 Subject: [PATCH 121/126] Revert "pass additional param into java world so don't show toast every time connect to relay" This reverts commit e9615cb15493c62c3791ad20143bad9c22af8cbb. --- xwords4/android/XWords4/jni/xportwrapper.c | 4 ++-- .../org/eehouse/android/xw4/BoardActivity.java | 17 +++++++---------- .../org/eehouse/android/xw4/CommsTransport.java | 7 +++---- .../org/eehouse/android/xw4/MultiMsgSink.java | 4 ++-- .../eehouse/android/xw4/jni/TransportProcs.java | 6 ++---- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/xwords4/android/XWords4/jni/xportwrapper.c b/xwords4/android/XWords4/jni/xportwrapper.c index 4a089f407..d18f04492 100644 --- a/xwords4/android/XWords4/jni/xportwrapper.c +++ b/xwords4/android/XWords4/jni/xportwrapper.c @@ -112,12 +112,12 @@ and_xport_relayConnd( void* closure, XP_UCHAR* const room, XP_Bool reconnect, AndTransportProcs* aprocs = (AndTransportProcs*)closure; if ( NULL != aprocs->jxport ) { JNIEnv* env = *aprocs->envp; - const char* sig = "(Ljava/lang/String;ZIZI)V"; + const char* sig = "(Ljava/lang/String;IZI)V"; jmethodID mid = getMethodID( env, aprocs->jxport, "relayConnd", sig ); jstring str = (*env)->NewStringUTF( env, room ); (*env)->CallVoidMethod( env, aprocs->jxport, mid, - str, reconnect, devOrder, allHere, nMissing ); + str, devOrder, allHere, nMissing ); deleteLocalRef( env, str ); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index e0e8775bb..eadcc7733 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -1030,17 +1030,14 @@ public class BoardActivity extends XWActivity // TransportProcs.TPMsgHandler interface ////////////////////////////////////////////////// - public void tpmRelayConnd( final String room, boolean isReconnect, - final int devOrder, final boolean allHere, - final int nMissing ) + public void tpmRelayConnd( final String room, final int devOrder, + final boolean allHere, final int nMissing ) { - if ( !isReconnect ) { - post( new Runnable() { - public void run() { - handleConndMessage( room, devOrder, allHere, nMissing ); - } - } ); - } + post( new Runnable() { + public void run() { + handleConndMessage( room, devOrder, allHere, nMissing ); + } + } ); } public void tpmRelayErrorProc( TransportProcs.XWRELAY_ERROR relayErr ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java index c50a836f1..ba89de474 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java @@ -435,11 +435,10 @@ public class CommsTransport implements TransportProcs, } } - public void relayConnd( String room, boolean isReconnect, int devOrder, - boolean allHere, int nMissing ) + public void relayConnd( String room, int devOrder, boolean allHere, + int nMissing ) { - m_tpHandler.tpmRelayConnd( room, isReconnect, devOrder, allHere, - nMissing ); + m_tpHandler.tpmRelayConnd( room, devOrder, allHere, nMissing ); } public void relayErrorProc( XWRELAY_ERROR relayErr ) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java index a4dff8698..9041220bc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/MultiMsgSink.java @@ -48,8 +48,8 @@ public class MultiMsgSink implements TransportProcs { { } - public void relayConnd( String room, boolean isReconnect, int devOrder, - boolean allHere, int nMissing ) + public void relayConnd( String room, int devOrder, boolean allHere, + int nMissing ) { } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java index b17c29fc3..013750586 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/TransportProcs.java @@ -37,8 +37,7 @@ public interface TransportProcs { }; void relayStatus( CommsRelayState newState ); - void relayConnd( String room, boolean isReconnect, int devOrder, - boolean allHere, int nMissing ); + void relayConnd( String room, int devOrder, boolean allHere, int nMissing ); public static enum XWRELAY_ERROR { NONE ,OLDFLAGS @@ -62,8 +61,7 @@ public interface TransportProcs { boolean relayNoConnProc( byte[] buf, String relayID ); public interface TPMsgHandler { - public void tpmRelayConnd( String room, boolean isReconnect, - int devOrder, boolean allHere, + public void tpmRelayConnd( String room, int devOrder, boolean allHere, int nMissing ); public void tpmRelayErrorProc( XWRELAY_ERROR relayErr ); } From e7d20fd2a3e3f7ca22d412c22bb19418fbaead87 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 8 Jul 2013 07:03:57 -0700 Subject: [PATCH 122/126] change API version configured by build script to be what I'm using now --- xwords4/android/XWords4/project.properties | 7 ++++--- xwords4/android/scripts/setup_local_props.sh | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/project.properties b/xwords4/android/XWords4/project.properties index 50c81c81a..15ad57d47 100644 --- a/xwords4/android/XWords4/project.properties +++ b/xwords4/android/XWords4/project.properties @@ -3,11 +3,12 @@ # # This file must be checked in Version Control Systems. # -# To customize properties used by the Ant build system use, +# To customize properties used by the Ant build system edit # "ant.properties", and override values to adapt the script to your # project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt -# Indicates whether an apk should be generated for each density. -split.density=false # Project target. target=Google Inc.:Google APIs:11 diff --git a/xwords4/android/scripts/setup_local_props.sh b/xwords4/android/scripts/setup_local_props.sh index 6e72c1f46..a0ddded71 100755 --- a/xwords4/android/scripts/setup_local_props.sh +++ b/xwords4/android/scripts/setup_local_props.sh @@ -2,7 +2,7 @@ set -u -e -TARGET="android-7" +TARGET="Google Inc.:Google APIs:11" usage() { echo "usage: $0 [--target TARGET]" @@ -24,8 +24,7 @@ while [ $# -ge 1 ]; do shift done -# create local.properties for 1.6 sdk (target id 4). Use 'android -# list targets' to get the full set. +# create local.properties android update project --path . --target $TARGET echo "local.properties looks like this:" From 11994d06f8b83630455ec5a7ae4784b5d8aa522f Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 8 Jul 2013 07:09:03 -0700 Subject: [PATCH 123/126] quote variable to make shell happy --- xwords4/android/scripts/setup_local_props.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/scripts/setup_local_props.sh b/xwords4/android/scripts/setup_local_props.sh index a0ddded71..02611d7ea 100755 --- a/xwords4/android/scripts/setup_local_props.sh +++ b/xwords4/android/scripts/setup_local_props.sh @@ -25,7 +25,7 @@ while [ $# -ge 1 ]; do done # create local.properties -android update project --path . --target $TARGET +android update project --path . --target "$TARGET" echo "local.properties looks like this:" echo "" From 7e361710461df4d3d24d274d9281880d7f1f12d8 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 8 Jul 2013 07:13:04 -0700 Subject: [PATCH 124/126] add comment --- xwords4/android/XWords4/AndroidManifest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 228fbfe4d..2aee6e784 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -26,6 +26,9 @@ android:versionName="@string/app_version" > + Date: Mon, 8 Jul 2013 07:13:48 -0700 Subject: [PATCH 125/126] target is now hard-coded since I reliably can't derive it from the manifest file. --- xwords4/android/scripts/arelease-clone.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/xwords4/android/scripts/arelease-clone.sh b/xwords4/android/scripts/arelease-clone.sh index 9e22875ba..032a37c86 100755 --- a/xwords4/android/scripts/arelease-clone.sh +++ b/xwords4/android/scripts/arelease-clone.sh @@ -8,12 +8,6 @@ usage () { exit 0 } -getSDK() { - LINE=$(grep 'android:minSdkVersion' ./AndroidManifest.xml) - SDK=$(echo $LINE | sed 's/^.*targetSdkVersion=\"\([0-9]*\)\".*$/\1/') - echo $SDK -} - TAG="" BRANCH="" VARIANT="XWords4" @@ -67,10 +61,10 @@ git clone $SRCDIR BUILD cd BUILD git checkout ${TAG}${BRANCH} cd ./xwords4/android/${VARIANT} -../scripts/setup_local_props.sh --target android-$(getSDK) +../scripts/setup_local_props.sh ../scripts/arelease.sh --variant ${VARIANT} mkdir -p /tmp/releases_${VARIANT} cp *.apk /tmp/releases_${VARIANT} cd $CURDIR -rm -rf $BUILDIR +# rm -rf $BUILDIR From db26c32c3e235cfc306564cb5d12adee9fe36552 Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 8 Jul 2013 07:14:42 -0700 Subject: [PATCH 126/126] revert accidental part of commit --- xwords4/android/scripts/arelease-clone.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/scripts/arelease-clone.sh b/xwords4/android/scripts/arelease-clone.sh index 032a37c86..a3a6754b4 100755 --- a/xwords4/android/scripts/arelease-clone.sh +++ b/xwords4/android/scripts/arelease-clone.sh @@ -67,4 +67,4 @@ mkdir -p /tmp/releases_${VARIANT} cp *.apk /tmp/releases_${VARIANT} cd $CURDIR -# rm -rf $BUILDIR +rm -rf $BUILDIR