diff --git a/xwords4/android/XWords4-dbg/AndroidManifest.xml b/xwords4/android/XWords4-dbg/AndroidManifest.xml index be9026852..f6e0ff8f4 100644 --- a/xwords4/android/XWords4-dbg/AndroidManifest.xml +++ b/xwords4/android/XWords4-dbg/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> diff --git a/xwords4/android/XWords4-dbg/build.xml b/xwords4/android/XWords4-dbg/build.xml index f443806ad..f967d813a 100644 --- a/xwords4/android/XWords4-dbg/build.xml +++ b/xwords4/android/XWords4-dbg/build.xml @@ -55,7 +55,7 @@ - + diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 5b0a4f455..6ac78f455 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/assets/changes.html b/xwords4/android/XWords4/assets/changes.html index 738a70a7d..d03180578 100644 --- a/xwords4/android/XWords4/assets/changes.html +++ b/xwords4/android/XWords4/assets/changes.html @@ -13,10 +13,10 @@ -

Crosswords 4.4 beta 99 release

+

Crosswords 4.4 beta 100 release

-

This is a quick-fix release for Bluetooth on Android - Marshmallow.

+

This is the first of two releases that together fix stalling + issues in network games.

Please take @@ -26,10 +26,9 @@

New with this release

    -
  • Work around "security" change that broke Bluetooth - invitations and gameplay on the latest Android release - ("Marshmallow").
  • -
  • Fix crash choosing menu at the wrong time
  • +
  • Fix to recognize (but not yet send) correct identifier for + game state. (Once this upgrade is out I'll do a new release that + also sends the correct identifier.)

(The full changelog diff --git a/xwords4/android/XWords4/build.xml b/xwords4/android/XWords4/build.xml index b3df20f90..2b6fa9806 100644 --- a/xwords4/android/XWords4/build.xml +++ b/xwords4/android/XWords4/build.xml @@ -55,7 +55,7 @@ - + diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index 67a68f93c..5888b1e47 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -1,6 +1,6 @@ -/* -*- compile-command: "cd ..; ../scripts/ndkbuild.sh -j3"; -*- */ +/* -*- compile-command: "find-and-ant.sh debug install"; -*- */ /* - * Copyright © 2009 - 2011 by Eric House (xwords@eehouse.org). All rights + * Copyright © 2009 - 2016 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -648,7 +648,7 @@ makeDict( MPFORMAL JNIEnv *env, DictMgrCtxt* dictMgr, JNIUtilCtxt* jniutil, jstr anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname ); - XP_U32 numEdges; + XP_U32 numEdges = 0; XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, bytesSize, &numEdges ); if ( !parses || (check && !checkSanity( &anddict->super, diff --git a/xwords4/android/XWords4/res/values/app_name.xml b/xwords4/android/XWords4/res/values/app_name.xml index 51e7b86fe..3acd45e4c 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 99 + 4.4 beta 100 diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 309715685..b15cc270e 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -1738,7 +1738,7 @@ Crosswords for Android, Version %1$s, rev %2$s, built on %3$s. - Copyright (C) 1998-2015 by Eric + Copyright (C) 1998-2016 by Eric House. This free/open source software is released under the GNU Public License. diff --git a/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml b/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml index eb2036ad1..ea84bf59c 100644 --- a/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml +++ b/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml @@ -1495,7 +1495,7 @@ Sdrowssorc rof ,diordna Noisrev %1$s, ver %2$s, tliub no %3$s. - Thgirypoc )C( 5102-8991 yb Cire + Thgirypoc )C( 6102-8991 yb Cire Esuoh. Siht nepo/eerf ecruos erawtfos si desaeler rednu eht UNG Cilbup Esnecil. diff --git a/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml b/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml index df599be4c..5e3147cc4 100644 --- a/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml +++ b/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml @@ -1495,7 +1495,7 @@ CROSSWORDS FOR ANDROID, VERSION %1$s, REV %2$s, BUILT ON %3$s. - COPYRIGHT (C) 1998-2015 BY ERIC + COPYRIGHT (C) 1998-2016 BY ERIC HOUSE. THIS FREE/OPEN SOURCE SOFTWARE IS RELEASED UNDER THE GNU PUBLIC LICENSE. diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetStateCache.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetStateCache.java index 4337c320b..abc73269a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetStateCache.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/NetStateCache.java @@ -48,7 +48,7 @@ public class NetStateCache { private static boolean s_netAvail = false; private static boolean s_isWifi; private static PvtBroadcastReceiver s_receiver; - private static final boolean s_onSim = XWApp.onEmulator(); + private static final boolean s_onSDKSim = Build.PRODUCT.contains("sdk"); // not genymotion public static void register( Context context, StateChangedIf proc ) { @@ -69,11 +69,23 @@ public class NetStateCache { public static boolean netAvail( Context context ) { initIfNot( context ); - boolean result = s_netAvail || s_onSim; - if ( BuildConfig.DEBUG ) { - checkSame( context, result ); + + // Cache is returning false negatives. Don't trust it. + if ( !s_netAvail ) { + boolean netAvail = getIsConnected( context ); + if ( netAvail ) { + String msg = "netAvail(): second-guessing successful!!!"; + DbgUtils.logf( msg ); + Utils.showToast( context, msg ); + s_netAvail = true; + if ( null != s_receiver ) { + s_receiver.notifyStateChanged( context ); + } + } } - DbgUtils.logf( "NetStateCache.netAvail() => %b", result ); + + boolean result = s_netAvail || s_onSDKSim; + DbgUtils.logf( "netAvail() => %b", result ); return result; } @@ -94,6 +106,19 @@ public class NetStateCache { } } + private static boolean getIsConnected( Context context ) + { + boolean result = false; + NetworkInfo ni = ((ConnectivityManager) + context.getSystemService( Context.CONNECTIVITY_SERVICE )) + .getActiveNetworkInfo(); + if ( null != ni && ni.isConnectedOrConnecting() ) { + result = true; + } + DbgUtils.logf( "NetStateCache.getConnected() => %b", result ); + return result; + } + private static void initIfNot( Context context ) { synchronized( s_haveReceiver ) { @@ -150,7 +175,7 @@ public class NetStateCache { } @Override - public void onReceive( final Context context, Intent intent ) + public void onReceive( Context context, Intent intent ) { DbgUtils.assertOnUIThread(); @@ -180,41 +205,7 @@ public class NetStateCache { if ( s_netAvail != netAvail ) { s_netAvail = netAvail; // keep current in case we're asked - - // We want to wait for WAIT_STABLE_MILLIS of inactivity - // before informing listeners. So each time there's a - // change, kill any existing timer then set another, which - // will only fire if we go that long without coming - // through here again. - - if ( null != mNotifyLater ) { - mHandler.removeCallbacks( mNotifyLater ); - mNotifyLater = null; - } - if ( mLastStateSent != s_netAvail ) { - mNotifyLater = new Runnable() { - @Override - public void run() { - Assert.assertTrue( mLastStateSent != s_netAvail ); - mLastStateSent = s_netAvail; - - synchronized( s_ifs ) { - Iterator iter = s_ifs.iterator(); - while ( iter.hasNext() ) { - iter.next().netAvail( s_netAvail ); - } - } - - if ( s_netAvail ) { - CommsConnType typ = CommsConnType - .COMMS_CONN_RELAY; - GameUtils.resendAllIf( context, typ, - false ); - } - } - }; - mHandler.postDelayed( mNotifyLater, WAIT_STABLE_MILLIS ); - } + notifyStateChanged( context ); } else { DbgUtils.logdf( "NetStateCache.PvtBroadcastReceiver.onReceive:" + " no change; doing nothing; s_netAvail=%b", @@ -222,6 +213,46 @@ public class NetStateCache { } } } + + public void notifyStateChanged( final Context context ) + { + // We want to wait for WAIT_STABLE_MILLIS of inactivity + // before informing listeners. So each time there's a + // change, kill any existing timer then set another, which + // will only fire if we go that long without coming + // through here again. + + if ( null != mNotifyLater ) { + mHandler.removeCallbacks( mNotifyLater ); + mNotifyLater = null; + } + if ( mLastStateSent != s_netAvail ) { + mNotifyLater = new Runnable() { + @Override + public void run() { + Assert.assertTrue( mLastStateSent != s_netAvail ); + mLastStateSent = s_netAvail; + + DbgUtils.logf( "NetStateCache.notifyStateChanged(%b)", + s_netAvail ); + + synchronized( s_ifs ) { + Iterator iter = s_ifs.iterator(); + while ( iter.hasNext() ) { + iter.next().netAvail( s_netAvail ); + } + } + + if ( s_netAvail ) { + CommsConnType typ = CommsConnType + .COMMS_CONN_RELAY; + GameUtils.resendAllIf( context, typ, false ); + } + } + }; + mHandler.postDelayed( mNotifyLater, WAIT_STABLE_MILLIS ); + } + } } // class PvtBroadcastReceiver } diff --git a/xwords4/android/scripts/Variant.mk b/xwords4/android/scripts/Variant.mk index 791b64287..bdf7c3924 100644 --- a/xwords4/android/scripts/Variant.mk +++ b/xwords4/android/scripts/Variant.mk @@ -6,6 +6,9 @@ $(DEST_PATH)/%.java : $(SRC_PATH)/%.java -e "s,\(import org.eehouse.android.\)xw4\(.*\);,\1$(VARIANT)\2;," \ < $< > $@ +$(DEST_PATH)/icon48x48.png : $(SRC_PATH)/icon48x48.png + convert $< -fill red -gravity Center -annotate +0+5 ' Dbg ' $@ + $(DEST_PATH)/%.png : $(SRC_PATH)/%.png @cp $< $@ diff --git a/xwords4/common/dbgutil.c b/xwords4/common/dbgutil.c index fefb804c1..d6bbec783 100644 --- a/xwords4/common/dbgutil.c +++ b/xwords4/common/dbgutil.c @@ -94,9 +94,9 @@ dbg_logstream( const XWStreamCtxt* stream, const char* func, int line ) if ( !!stream ) { XP_U16 len = 0; XWStreamPos end = stream_getPos( stream, POS_WRITE ); - stream_copyBits( stream, 0, end, NULL, &len ); + stream_copyBits( stream, end, NULL, &len ); XP_U8 buf[len]; - stream_copyBits( stream, 0, end, buf, &len ); + stream_copyBits( stream, end, buf, &len ); char comment[128]; XP_SNPRINTF( comment, VSIZE(comment), "%s line %d", func, line ); LOG_HEX( buf, len, comment ); diff --git a/xwords4/common/memstream.c b/xwords4/common/memstream.c index fcf62c47b..43240920a 100644 --- a/xwords4/common/memstream.c +++ b/xwords4/common/memstream.c @@ -25,6 +25,7 @@ #include "comtypes.h" #include "memstream.h" #include "vtabmgr.h" +#include "strutils.h" #ifdef CPLUS extern "C" { @@ -184,10 +185,10 @@ mem_stream_getBits( XWStreamCtxt* p_sctx, XP_U16 nBits ) return result; } /* stream_getBits */ -#if defined HASH_STREAM || defined DEBUG +#if defined DEBUG static void -mem_stream_copyBits( const XWStreamCtxt* p_sctx, XWStreamPos XP_UNUSED(startPos), - XWStreamPos endPos, XP_U8* buf, XP_U16* lenp ) +mem_stream_copyBits( const XWStreamCtxt* p_sctx, XWStreamPos endPos, + XP_U8* buf, XP_U16* lenp ) { MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx; XP_U16 len = BYTE_PART(endPos); @@ -195,7 +196,7 @@ mem_stream_copyBits( const XWStreamCtxt* p_sctx, XWStreamPos XP_UNUSED(startPos) XP_ASSERT( len <= stream->nBytesAllocated ); XP_MEMCPY( buf, stream->buf, len ); if ( 0 != BIT_PART(endPos) ) { - buf[len-1] &= 1 << BIT_PART(endPos); + buf[len-1] &= ~(0xFF << BIT_PART(endPos)); } } *lenp = len; @@ -371,6 +372,36 @@ mem_stream_getSize( const XWStreamCtxt* p_sctx ) return size; } /* mem_stream_getSize */ +static XP_U32 +mem_stream_getHash( const XWStreamCtxt* p_sctx, XWStreamPos pos, + XP_Bool correct ) +{ + XP_U32 hash = 0; + const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx; + const XP_U8* ptr = stream->buf; + XP_U16 len = BYTE_PART(pos); + XP_U16 bits = BIT_PART(pos); + if ( 0 != bits ) { + XP_ASSERT( 0 < len ); + --len; + } + + hash = augmentHash( 0, ptr, len ); + if ( 0 != bits ) { + XP_U8 byt = ptr[len]; + if ( correct ) { + byt &= ~(0xFF << bits); + } else { + byt &= 1 << bits; + } + hash = augmentHash( hash, &byt, 1 ); + } + hash = finishHash( hash ); + + LOG_RETURNF( "%X(%d:%d)", hash, len, bits ); + return hash; +} /* mem_stream_getHash */ + static const XP_U8* mem_stream_getPtr( const XWStreamCtxt* p_sctx ) { @@ -476,7 +507,7 @@ make_vtable( MemStreamCtxt* stream ) SET_VTABLE_ENTRY( vtable, stream_getU16, mem ); SET_VTABLE_ENTRY( vtable, stream_getU32, mem ); SET_VTABLE_ENTRY( vtable, stream_getBits, mem ); -#if defined HASH_STREAM || defined DEBUG +#if defined DEBUG SET_VTABLE_ENTRY( vtable, stream_copyBits, mem ); #endif @@ -497,6 +528,7 @@ make_vtable( MemStreamCtxt* stream ) SET_VTABLE_ENTRY( vtable, stream_close, mem ); SET_VTABLE_ENTRY( vtable, stream_getSize, mem ); + SET_VTABLE_ENTRY( vtable, stream_getHash, mem ); SET_VTABLE_ENTRY( vtable, stream_getPtr, mem ); SET_VTABLE_ENTRY( vtable, stream_getAddress, mem ); SET_VTABLE_ENTRY( vtable, stream_setAddress, mem ); diff --git a/xwords4/common/model.c b/xwords4/common/model.c index 2fb803b46..12fcfaa7b 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -1,6 +1,6 @@ /* -*- compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */ /* - * Copyright 2000-2011 by Eric House (xwords@eehouse.org). All rights + * Copyright 2000-2015 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -312,36 +312,67 @@ model_destroy( ModelCtxt* model ) } /* model_destroy */ XP_U32 -model_getHash( const ModelCtxt* model, XP_U16 version ) +model_getHash( const ModelCtxt* model ) { #ifndef STREAM_VERS_HASHSTREAM XP_USE(version); #endif StackCtxt* stack = model->vol.stack; XP_ASSERT( !!stack ); - XP_U32 hash = -#ifdef STREAM_VERS_HASHSTREAM - STREAM_VERS_HASHSTREAM <= version ? - stack_getHash( stack ) : -#endif - stack_getHashOld( stack ); - /* XP_LOGF( "%s(version=%x)=>%.8X", __func__, version, */ - /* (unsigned int)hash ); */ - return hash; + return stack_getHash( stack, XP_FALSE ); } XP_Bool model_hashMatches( const ModelCtxt* model, const XP_U32 hash ) { StackCtxt* stack = model->vol.stack; - XP_Bool matches = -#ifdef STREAM_VERS_HASHSTREAM - (hash == stack_getHash( stack )) || -#endif - (hash == stack_getHashOld( stack )); + XP_Bool matches = hash == stack_getHash( stack, XP_TRUE ) + || hash == stack_getHash( stack, XP_FALSE ); return matches; } +XP_Bool +model_popToHash( ModelCtxt* model, const XP_U32 hash, PoolContext* pool ) +{ + XP_U16 nPopped = 0; + StackCtxt* stack = model->vol.stack; + const XP_U16 nEntries = stack_getNEntries( stack ); + StackEntry entries[nEntries]; + XP_S16 foundAt = -1; + + for ( XP_U16 ii = 0; ii < nEntries; ++ii ) { + if ( hash == stack_getHash( stack, XP_TRUE ) + || hash == stack_getHash( stack, XP_FALSE ) ) { + foundAt = ii; + break; + } + if ( ! stack_popEntry( stack, &entries[ii] ) ) { + break; + } + ++nPopped; + } + + for ( XP_S16 ii = nPopped - 1; ii >= 0; --ii ) { + stack_redo( stack, &entries[ii] ); + } + + XP_Bool found = -1 != foundAt; + if ( found ) { + XP_LOGF( "%s: undoing %d turns to match hash %X", __func__, + foundAt, hash ); +#ifdef DEBUG + XP_Bool success = +#endif + model_undoLatestMoves( model, pool, foundAt, NULL, NULL ); + XP_ASSERT( success ); + /* Assert not needed for long */ + XP_ASSERT( hash == stack_getHash( model->vol.stack, XP_TRUE ) + || hash == stack_getHash( model->vol.stack, XP_FALSE ) ); + } + + return found; +} + #ifdef STREAM_VERS_BIGBOARD void model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses, XP_U16 nBonuses ) diff --git a/xwords4/common/model.h b/xwords4/common/model.h index c58f95ded..d672e9e47 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -122,8 +122,11 @@ void model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream ); void model_setSize( ModelCtxt* model, XP_U16 boardSize ); void model_destroy( ModelCtxt* model ); -XP_U32 model_getHash( const ModelCtxt* model, XP_U16 version ); +XP_U32 model_getHash( const ModelCtxt* model ); XP_Bool model_hashMatches( const ModelCtxt* model, XP_U32 hash ); +XP_Bool model_popToHash( ModelCtxt* model, const XP_U32 hash, + PoolContext* pool ); + void model_setNPlayers( ModelCtxt* model, XP_U16 numPlayers ); XP_U16 model_getNPlayers( const ModelCtxt* model ); diff --git a/xwords4/common/movestak.c b/xwords4/common/movestak.c index 401927347..ee94b4803 100644 --- a/xwords4/common/movestak.c +++ b/xwords4/common/movestak.c @@ -1,6 +1,6 @@ -/* -*- compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */ +/* -*- compile-command: "cd ../linux && make -j5 MEMDEBUG=TRUE"; -*- */ /* - * Copyright 2001, 2006-2012 by Eric House (xwords@eehouse.org). All rights + * Copyright 2001-2015 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -67,88 +67,14 @@ stack_init( StackCtxt* stack ) shrunk to fit as soon as we serialize/deserialize anyway. */ } /* stack_init */ -static XP_U32 -augmentHash( XP_U32 hash, const XP_U8* ptr, XP_U16 len ) -{ - XP_ASSERT( 0 < len ); - // see http://en.wikipedia.org/wiki/Jenkins_hash_function - XP_U16 ii; - for ( ii = 0; ii < len; ++ii ) { - hash += *ptr++; - hash += (hash << 10); - hash ^= (hash >> 6); - } - // XP_LOGF( "%s: hashed %d bytes -> %X", __func__, len, (unsigned int)hash ); - return hash; -} - -static XP_U32 -finishHash( XP_U32 hash ) -{ - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; -} - -static XP_U32 -augmentFor( XP_U32 hash, const StackEntry* entry ) -{ - switch( entry->moveType ) { - case ASSIGN_TYPE: { - TrayTileSet tiles; - sortTiles( &tiles, &entry->u.assign.tiles, 0 ); - hash = augmentHash( hash, (XP_U8*)&tiles, sizeof(tiles) ); - } - break; - case MOVE_TYPE: - hash = augmentHash( hash, (XP_U8*)&entry->u.move, - sizeof(entry->u.move) ); - break; - case TRADE_TYPE: - hash = augmentHash( hash, (XP_U8*)&entry->u.trade, - sizeof(entry->u.trade) ); - break; - case PHONY_TYPE: - hash = augmentHash( hash, (XP_U8*)&entry->u.phony, - sizeof(entry->u.phony) ); - break; - } - return hash; -} - -XP_U32 -stack_getHashOld( StackCtxt* stack ) -{ - XP_U16 nn, nEntries = stack->nEntries; - XP_U32 hash = 0L; - for ( nn = 0; nn < nEntries; ++nn ) { - StackEntry entry; - XP_MEMSET( &entry, 0, sizeof(entry) ); - if ( !stack_getNthEntry( stack, nn, &entry ) ) { - XP_ASSERT( 0 ); - } - hash = augmentFor( hash, &entry ); - // XP_LOGF( "hash after %d: %.8X", nn, (unsigned int)hash ); - } - XP_ASSERT( 0 != hash ); - hash = finishHash( hash ); - LOG_RETURNF( "%.8X", (unsigned int)hash ); - return hash; -} /* stack_getHashOld */ - #ifdef STREAM_VERS_HASHSTREAM XP_U32 -stack_getHash( const StackCtxt* stack ) +stack_getHash( const StackCtxt* stack, XP_Bool correct ) { - XP_U32 hash; - XP_U16 len = 0; - stream_copyBits( stack->data, 0, stack->top, NULL, &len ); - XP_U8 buf[len]; - stream_copyBits( stack->data, 0, stack->top, buf, &len ); - // LOG_HEX( buf, len, __func__ ); - hash = finishHash( augmentHash( 0L, buf, len ) ); - // LOG_RETURNF( "%.8X", (unsigned int)hash ); + XP_U32 hash = 0; + if ( !!stack->data ) { + hash = stream_getHash( stack->data, stack->top, correct ); + } return hash; } /* stack_getHash */ #endif @@ -250,7 +176,7 @@ stack_copy( const StackCtxt* stack ) } static void -pushEntry( StackCtxt* stack, const StackEntry* entry ) +pushEntryImpl( StackCtxt* stack, const StackEntry* entry ) { XP_U16 ii, bitsPerTile; XWStreamPos oldLoc; @@ -308,9 +234,33 @@ pushEntry( StackCtxt* stack, const StackEntry* entry ) ++stack->nEntries; stack->highWaterMark = stack->nEntries; stack->top = stream_setPos( stream, POS_WRITE, oldLoc ); - // XP_LOGSTREAM( stack->data ); +#ifdef DEBUG_HASHING + XP_LOGSTREAM( stack->data ); +#endif SET_DIRTY( stack ); -} /* pushEntry */ +} /* pushEntryImpl */ + +static void +pushEntry( StackCtxt* stack, const StackEntry* entry ) +{ +#ifdef DEBUG_HASHING + XP_Bool correct = XP_TRUE; + XP_U32 origHash = stack_getHash( stack, correct ); +#endif + + pushEntryImpl( stack, entry ); + +#ifdef DEBUG_HASHING + XP_U32 newHash = stack_getHash( stack, XP_TRUE ); + StackEntry lastEntry; + if ( stack_popEntry( stack, &lastEntry ) ) { + XP_ASSERT( origHash == stack_getHash( stack, correct ) ); + pushEntryImpl( stack, &lastEntry ); + XP_ASSERT( newHash == stack_getHash( stack, correct ) ); + XP_LOGF( "%s: all ok", __func__ ); + } +#endif +} static void readEntry( const StackCtxt* stack, StackEntry* entry ) @@ -482,7 +432,9 @@ stack_popEntry( StackCtxt* stack, StackEntry* entry ) setCacheReadyFor( stack, nn ); /* set cachedPos by side-effect */ stack->top = stack->cachedPos; } - // XP_LOGSTREAM( stack->data ); +#ifdef DEBUG_HASHING + XP_LOGSTREAM( stack->data ); +#endif return found; } /* stack_popEntry */ diff --git a/xwords4/common/movestak.h b/xwords4/common/movestak.h index a2a85f72d..2845f628a 100644 --- a/xwords4/common/movestak.h +++ b/xwords4/common/movestak.h @@ -69,8 +69,7 @@ StackCtxt* stack_make( MPFORMAL VTableMgr* vtmgr ); void stack_destroy( StackCtxt* stack ); void stack_init( StackCtxt* stack ); -XP_U32 stack_getHashOld( StackCtxt* stack ); -XP_U32 stack_getHash( const StackCtxt* stack ); +XP_U32 stack_getHash( const StackCtxt* stack, XP_Bool correct ); void stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile ); void stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream ); diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 0e3b09719..20e867fad 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -2032,8 +2032,10 @@ sendMoveTo( ServerCtxt* server, XP_U16 devIndex, XP_U16 turn, XP_U16 version = stream_getVersion( stream ); if ( STREAM_VERS_BIGBOARD <= version ) { XP_ASSERT( version == server->nv.streamVersion ); - XP_U32 hash = model_getHash( server->vol.model, version ); - // XP_LOGF( "%s: adding hash %x", __func__, (unsigned int)hash ); + XP_U32 hash = model_getHash( server->vol.model ); +#ifdef DEBUG_HASHING + XP_LOGF( "%s: adding hash %x", __func__, (unsigned int)hash ); +#endif stream_putU32( stream, hash ); } #endif @@ -2084,10 +2086,16 @@ readMoveInfo( ServerCtxt* server, XWStreamCtxt* stream, #ifdef STREAM_VERS_BIGBOARD if ( STREAM_VERS_BIGBOARD <= stream_getVersion( stream ) ) { XP_U32 hashReceived = stream_getU32( stream ); - success = model_hashMatches( server->vol.model, hashReceived ); - if ( !success ) { - XP_LOGF( "%s: hash mismatch",__func__); + success = model_hashMatches( server->vol.model, hashReceived ) + || model_popToHash( server->vol.model, hashReceived, server->pool ); + // XP_ASSERT( success ); /* I need to understand when this can fail */ +#ifdef DEBUG_HASHING + if ( success ) { + XP_LOGF( "%s: hash match: %X",__func__, hashReceived ); + } else { + XP_LOGF( "%s: hash mismatch: %X not found",__func__, hashReceived ); } +#endif } #endif if ( success ) { diff --git a/xwords4/common/strutils.c b/xwords4/common/strutils.c index c1485aefd..9ba8a6da3 100644 --- a/xwords4/common/strutils.c +++ b/xwords4/common/strutils.c @@ -228,6 +228,30 @@ p_replaceStringIfDifferent( MPFORMAL XP_UCHAR** curLoc, const XP_UCHAR* newStr *curLoc = curStr; } /* replaceStringIfDifferent */ +XP_U32 +augmentHash( XP_U32 hash, const XP_U8* ptr, XP_U16 len ) +{ + // see http://en.wikipedia.org/wiki/Jenkins_hash_function + for ( XP_U16 ii = 0; ii < len; ++ii ) { + hash += *ptr++; + hash += (hash << 10); + hash ^= (hash >> 6); + } +#ifdef DEBUG_HASHING + XP_LOGF( "%s: hashed %d bytes -> %X", __func__, len, (unsigned int)hash ); +#endif + return hash; +} + +XP_U32 +finishHash( XP_U32 hash ) +{ + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + /* * A wrapper for printing etc. potentially null strings. */ diff --git a/xwords4/common/strutils.h b/xwords4/common/strutils.h index 00e2e6116..1451e6dc7 100644 --- a/xwords4/common/strutils.h +++ b/xwords4/common/strutils.h @@ -68,6 +68,8 @@ XP_UCHAR* p_copyString( MPFORMAL const XP_UCHAR* instr # define copyString( p, in ) p_copyString( in ) #endif +XP_U32 augmentHash( XP_U32 hash, const XP_U8* ptr, XP_U16 len ); +XP_U32 finishHash( XP_U32 hash ); void p_replaceStringIfDifferent( MPFORMAL XP_UCHAR** curLoc, const XP_UCHAR* newStr diff --git a/xwords4/common/xwstream.h b/xwords4/common/xwstream.h index dc5dfd060..a9177e644 100644 --- a/xwords4/common/xwstream.h +++ b/xwords4/common/xwstream.h @@ -1,6 +1,7 @@ /* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* - * Copyright 1997 - 2000 by Eric House (xwords@eehouse.org). All rights reserved. + * Copyright 1997 - 2015 by Eric House (xwords@eehouse.org). All rights + * reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,9 +49,9 @@ typedef struct StreamCtxVTable { XP_U16 (*m_stream_getU16)( XWStreamCtxt* dctx ); XP_U32 (*m_stream_getU32)( XWStreamCtxt* dctx ); XP_U32 (*m_stream_getBits)( XWStreamCtxt* dctx, XP_U16 nBits ); -#if defined HASH_STREAM || defined DEBUG - void (*m_stream_copyBits)( const XWStreamCtxt* dctx, XWStreamPos startPos, - XWStreamPos endPos, XP_U8* buf, XP_U16* len ); +#if defined DEBUG + void (*m_stream_copyBits)( const XWStreamCtxt* dctx, XWStreamPos endPos, + XP_U8* buf, XP_U16* len ); #endif void (*m_stream_putU8)( XWStreamCtxt* dctx, XP_U8 byt ); @@ -73,6 +74,8 @@ typedef struct StreamCtxVTable { void (*m_stream_close)( XWStreamCtxt* dctx ); XP_U16 (*m_stream_getSize)( const XWStreamCtxt* dctx ); + XP_U32 (*m_stream_getHash)( const XWStreamCtxt* dctx, XWStreamPos pos, + XP_Bool correct ); const XP_U8* (*m_stream_getPtr)( const XWStreamCtxt* dctx ); @@ -113,9 +116,9 @@ struct XWStreamCtxt { #define stream_getBits(sc, n) \ (sc)->vtable->m_stream_getBits((sc), (n)) -#if defined HASH_STREAM || defined DEBUG -#define stream_copyBits(sc, s, e, b, l) \ - (sc)->vtable->m_stream_copyBits((sc), (s), (e), (b), (l)) +#if defined DEBUG +#define stream_copyBits(sc, e, b, l) \ + (sc)->vtable->m_stream_copyBits((sc), (e), (b), (l)) #endif #define stream_putU8(sc, b) \ @@ -154,6 +157,9 @@ struct XWStreamCtxt { #define stream_getSize(sc) \ (sc)->vtable->m_stream_getSize((sc)) +#define stream_getHash(sc, p, c) \ + (sc)->vtable->m_stream_getHash((sc), (p), (c)) + #define stream_getPtr(sc) \ (sc)->vtable->m_stream_getPtr((sc)) diff --git a/xwords4/dawg/French/Makefile.ODS7 b/xwords4/dawg/French/Makefile.ODS7 new file mode 100644 index 000000000..a5811b078 --- /dev/null +++ b/xwords4/dawg/French/Makefile.ODS7 @@ -0,0 +1,32 @@ +# -*-mode: Makefile -*- +# Copyright 2002 by Eric House (xwords@eehouse.org). All rights reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +XWLANG=ODS7 +LANGCODE=fr_FR + +TARGET_TYPE ?= WINCE + +include ../Makefile.langcommon + +$(XWLANG)Main.dict.gz: $(XWDICTPATH)/French/ods7.txt + cat $< | tr -d '\r' | tr a-z A-Z | grep '^[A-Z]*$$' | gzip >$@ + +# Everything but creating of the Main.dict file is inherited from the +# "parent" Makefile.langcommon in the parent directory. + +clean: clean_common + rm -f $(XWLANG)Main.dict.gz *.bin $(XWLANG)*.pdb $(XWLANG)*.seb diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index 2297e1b62..151cab0ed 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -22,6 +22,7 @@ ifeq ($(MEMDEBUG),TRUE) DEFINES = -DMEM_DEBUG -DDEBUG -DENABLE_LOGGING -DNUMBER_KEY_AS_INDEX DEFINES += -DCOMMS_CHECKSUM CFLAGS += -g $(GPROFFLAG) -Wall -Wunused-parameter -Wcast-align -Werror -O0 +# DEFINES += -DDEBUG_HASHING CFLAGS += -DDEBUG_TS -rdynamic PLATFORM = obj_linux_memdbg else @@ -119,7 +120,7 @@ DEFINES += -DXWFEATURE_DEVID DEFINES += -DXWFEATURE_COMMSACK #DEFINES += -DXWFEATURE_ACTIVERECT DEFINES += -DCOMMS_XPORT_FLAGSPROC -DEFINES += -DINITIAL_CLIENT_VERS=2 +DEFINES += -DINITIAL_CLIENT_VERS=3 DEFINES += -DCOMMON_LAYOUT DEFINES += -DNATIVE_NLI diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 21d742251..78029e665 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -554,7 +554,7 @@ secondTimerFired( gpointer data ) if ( !!game->server && !!game->board ) { XP_U16 undoRatio = cGlobals->params->undoRatio; if ( 0 != undoRatio ) { - if ( (XP_RANDOM() % 100) < undoRatio ) { + if ( (XP_RANDOM() % 1000) < undoRatio ) { XP_LOGF( "%s: calling server_handleUndo", __func__ ); if ( server_handleUndo( game->server, 1 ) ) { board_draw( game->board ); @@ -2443,8 +2443,8 @@ main( int argc, char** argv ) #ifdef USE_GLIBLOOP case CMD_UNDOPCT: mainParams.undoRatio = atoi( optarg ); - if ( mainParams.undoRatio < 0 || mainParams.undoRatio > 100 ) { - usage(argv[0], "must be 0 <= n <= 100" ); + if ( mainParams.undoRatio < 0 || mainParams.undoRatio > 1000 ) { + usage(argv[0], "must be 0 <= n <= 1000" ); } break; #endif diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 1c8dc00c3..19d914e7c 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -6,7 +6,7 @@ APP_NEW="" DO_CLEAN="" APP_NEW_PARAMS="" NGAMES="" -UDP_PCT_START=5 +UDP_PCT_START=100 UDP_PCT_INCR=10 UPGRADE_ODDS="" NROOMS="" @@ -68,7 +68,7 @@ function cleanup() { function connName() { LOG=$1 - grep 'got_connect_cmd: connName' $LOG | \ + grep -a 'got_connect_cmd: connName' $LOG | \ tail -n 1 | \ sed 's,^.*connName: \"\(.*\)\" (reconnect=.)$,\1,' } @@ -356,7 +356,7 @@ maybe_resign() { if [ "$RESIGN_RATIO" -gt 0 ]; then KEY=$1 LOG=${LOGS[$KEY]} - if grep -q XWRELAY_ALLHERE $LOG; then + if grep -aq XWRELAY_ALLHERE $LOG; then if [ 0 -eq $(($RANDOM % $RESIGN_RATIO)) ]; then echo "making $LOG $(connName $LOG) resign..." kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" || /bin/true @@ -397,14 +397,13 @@ check_game() { CONNNAME="$(connName $LOG)" OTHERS="" if [ -n "$CONNNAME" ]; then - if grep -q '\[unused tiles\]' $LOG; then - ALL_DONE=TRUE + if grep -aq '\[unused tiles\]' $LOG ; then for INDX in ${!LOGS[*]}; do [ $INDX -eq $KEY ] && continue ALOG=${LOGS[$INDX]} CONNNAME2="$(connName $ALOG)" if [ "$CONNNAME2" = "$CONNNAME" ]; then - if ! grep -q '\[unused tiles\]' $ALOG; then + if ! grep -aq '\[unused tiles\]' $ALOG; then OTHERS="" break fi @@ -424,11 +423,11 @@ check_game() { done echo "" # XWRELAY_ERROR_DELETED may be old - elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then + elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then echo "deleting $LOG $(connName $LOG) b/c another resigned" kill_from_log $LOG || /bin/true close_device $KEY $DEADDIR "other resigned" - elif grep -q 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then + elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then echo "deleting $LOG $(connName $LOG) b/c another resigned" kill_from_log $LOG || /bin/true close_device $KEY $DEADDIR "other resigned" @@ -474,7 +473,7 @@ summarizeTileCounts() { for KEY in ${KEYS[@]}; do local LOG=${LOGS[$KEY]} - local LINE=$(grep pool_removeTiles $LOG | tail -n 1) + local LINE=$(grep -a pool_removeTiles $LOG | tail -n 1) if [ -n "$LINE" ]; then local NUM=$(echo $LINE | sed 's,^.*removeTiles: \(.*\) tiles.*$,\1,') STR="${STR} ${KEY}:${NUM}" @@ -482,7 +481,7 @@ summarizeTileCounts() { done if [ -n "${STR}" ]; then - echo "$(date +%r) tiles left: $STR" + echo "** $(date +%r) tiles left: $STR" fi } @@ -613,7 +612,7 @@ function usage() { echo " [--seed ] \\" >&2 echo " [--send-chat \\" >&2 echo " [--udp-incr ] \\" >&2 - echo " [--udp-start ] \\" >&2 + echo " [--udp-start ] # default: $UDP_PCT_START \\" >&2 echo " [--undo-pct ] \\" >&2 exit 1