wrap jni ptr so can detect leaks (failure to call

game_dispose()). There was one that had been undetected for years. The
int is still passed into the jni, and each call site has to pull it
from the wrapper. Better would be to pass the wrapper, but I'm worried
about the performance hit of making a call back into the java world
from every jni entrypoint. Will test....
This commit is contained in:
Eric House 2016-01-31 19:51:51 -08:00
parent 8327fa71a1
commit 9455c0ef26
9 changed files with 190 additions and 154 deletions

View file

@ -109,8 +109,10 @@ map_thread( EnvThreadInfo* ti, JNIEnv* env )
XP_ASSERT( !!firstEmpty ); XP_ASSERT( !!firstEmpty );
firstEmpty->owner = self; firstEmpty->owner = self;
firstEmpty->env = env; firstEmpty->env = env;
/* XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__, */ #ifdef LOG_MAPPING
/* firstEmpty - ti->entries, env, (int)self ); */ XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__,
firstEmpty - ti->entries, env, (int)self );
#endif
} }
pthread_mutex_unlock( &ti->mtxThreads ); pthread_mutex_unlock( &ti->mtxThreads );
@ -134,6 +136,11 @@ map_remove( EnvThreadInfo* ti, JNIEnv* env )
found = env == ti->entries[ii].env; found = env == ti->entries[ii].env;
if ( found ) { if ( found ) {
XP_ASSERT( pthread_self() == ti->entries[ii].owner ); XP_ASSERT( pthread_self() == ti->entries[ii].owner );
#ifdef LOG_MAPPING
XP_LOGF( "%s: UNMAPPED env %p to thread %x", __func__,
ti->entries[ii].env,
(int)ti->entries[ii].owner );
#endif
ti->entries[ii].env = NULL; ti->entries[ii].env = NULL;
ti->entries[ii].owner = 0; ti->entries[ii].owner = 0;
} }

View file

@ -61,6 +61,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.JNIThread.*; import org.eehouse.android.xw4.jni.JNIThread.*;
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
public class BoardDelegate extends DelegateBase public class BoardDelegate extends DelegateBase
implements TransportProcs.TPMsgHandler, View.OnClickListener, implements TransportProcs.TPMsgHandler, View.OnClickListener,
@ -83,7 +84,7 @@ public class BoardDelegate extends DelegateBase
private Activity m_activity; private Activity m_activity;
private BoardView m_view; private BoardView m_view;
private int m_jniGamePtr; private GamePtr m_jniGamePtr;
private GameLock m_gameLock; private GameLock m_gameLock;
private CurGameInfo m_gi; private CurGameInfo m_gi;
private GameSummary m_summary; private GameSummary m_summary;
@ -865,8 +866,8 @@ public class BoardDelegate extends DelegateBase
int id = item.getItemId(); int id = item.getItemId();
switch ( id ) { switch ( id ) {
case R.id.board_menu_done: case R.id.board_menu_done:
int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr, int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr.ptr(),
m_view.getCurPlayer() ); m_view.getCurPlayer() );
if ( XWApp.MAX_TRAY_TILES > nTiles ) { if ( XWApp.MAX_TRAY_TILES > nTiles ) {
showNotAgainDlgThen( R.string.not_again_done, showNotAgainDlgThen( R.string.not_again_done,
R.string.key_notagain_done, R.string.key_notagain_done,
@ -1415,9 +1416,9 @@ public class BoardDelegate extends DelegateBase
private void dropRelayAndRestart() { private void dropRelayAndRestart() {
CommsAddrRec addr = new CommsAddrRec(); CommsAddrRec addr = new CommsAddrRec();
XwJNI.comms_getAddr( m_jniGamePtr, addr ); XwJNI.comms_getAddr( m_jniGamePtr.ptr(), addr );
addr.remove( CommsConnType.COMMS_CONN_RELAY ); addr.remove( CommsConnType.COMMS_CONN_RELAY );
XwJNI.comms_setAddr( m_jniGamePtr, addr ); XwJNI.comms_setAddr( m_jniGamePtr.ptr(), addr );
finish(); finish();
@ -1642,7 +1643,7 @@ public class BoardDelegate extends DelegateBase
public void playerScoreHeld( int player ) public void playerScoreHeld( int player )
{ {
LastMoveInfo lmi = new LastMoveInfo(); LastMoveInfo lmi = new LastMoveInfo();
XwJNI.model_getPlayersLastScore( m_jniGamePtr, player, lmi ); XwJNI.model_getPlayersLastScore( m_jniGamePtr.ptr(), player, lmi );
String expl = lmi.format( m_activity ); String expl = lmi.format( m_activity );
if ( null == expl || 0 == expl.length() ) { if ( null == expl || 0 == expl.length() ) {
expl = getString( R.string.no_moves_made ); expl = getString( R.string.no_moves_made );
@ -2017,7 +2018,7 @@ public class BoardDelegate extends DelegateBase
private void loadGame( boolean isStart ) private void loadGame( boolean isStart )
{ {
if ( 0 == m_jniGamePtr ) { if ( null == m_jniGamePtr ) {
try { try {
String gameName = DBUtils.getName( m_activity, m_rowid ); String gameName = DBUtils.getName( m_activity, m_rowid );
String[] dictNames = GameUtils.dictNames( m_activity, m_rowid ); String[] dictNames = GameUtils.dictNames( m_activity, m_rowid );
@ -2043,19 +2044,19 @@ public class BoardDelegate extends DelegateBase
m_jniGamePtr = XwJNI.initJNI( m_rowid ); m_jniGamePtr = XwJNI.initJNI( m_rowid );
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_xport = new CommsTransport( m_jniGamePtr, m_activity, this, m_xport = new CommsTransport( m_activity, this, m_rowid,
m_rowid, m_gi.serverRole ); m_gi.serverRole );
} }
CommonPrefs cp = CommonPrefs.get( m_activity ); CommonPrefs cp = CommonPrefs.get( m_activity );
if ( null == stream || if ( null == stream ||
! XwJNI.game_makeFromStream( m_jniGamePtr, stream, ! XwJNI.game_makeFromStream( m_jniGamePtr.ptr(), stream,
m_gi, dictNames, m_gi, dictNames,
pairs.m_bytes, pairs.m_bytes,
pairs.m_paths, langName, pairs.m_paths, langName,
m_utils, m_jniu, m_utils, m_jniu,
null, cp, m_xport ) ) { null, cp, m_xport ) ) {
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, XwJNI.game_makeNewGame( m_jniGamePtr.ptr(), m_gi, m_utils,
m_jniu, null, cp, m_xport, m_jniu, null, cp, m_xport,
dictNames, pairs.m_bytes, dictNames, pairs.m_bytes,
pairs.m_paths, langName ); pairs.m_paths, langName );
@ -2212,7 +2213,7 @@ public class BoardDelegate extends DelegateBase
{ {
if ( null != m_connTypes if ( null != m_connTypes
&& m_connTypes.contains( CommsConnType.COMMS_CONN_BT ) ) { && m_connTypes.contains( CommsConnType.COMMS_CONN_BT ) ) {
CommsAddrRec[] addrs = XwJNI.comms_getAddrs( m_jniGamePtr ); CommsAddrRec[] addrs = XwJNI.comms_getAddrs( m_jniGamePtr.ptr() );
for ( CommsAddrRec addr : addrs ) { for ( CommsAddrRec addr : addrs ) {
if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) { if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) {
BTService.pingHost( m_activity, addr.bt_btAddr, BTService.pingHost( m_activity, addr.bt_btAddr,
@ -2370,7 +2371,7 @@ public class BoardDelegate extends DelegateBase
private void waitCloseGame( boolean save ) private void waitCloseGame( boolean save )
{ {
if ( 0 != m_jniGamePtr ) { if ( null != m_jniGamePtr ) {
if ( null != m_xport ) { if ( null != m_xport ) {
m_xport.waitToStop(); m_xport.waitToStop();
m_xport = null; m_xport = null;
@ -2394,8 +2395,8 @@ public class BoardDelegate extends DelegateBase
DBUtils.saveThumbnail( m_activity, m_gameLock, thumb ); DBUtils.saveThumbnail( m_activity, m_gameLock, thumb );
} }
XwJNI.game_dispose( m_jniGamePtr ); m_jniGamePtr.release();
m_jniGamePtr = 0; m_jniGamePtr = null;
m_gi = null; m_gi = null;
m_gameLock.unlock(); m_gameLock.unlock();
@ -2638,7 +2639,7 @@ public class BoardDelegate extends DelegateBase
private static boolean doRematchIf( Activity activity, DelegateBase dlgt, private static boolean doRematchIf( Activity activity, DelegateBase dlgt,
long rowid, GameSummary summary, long rowid, GameSummary summary,
CurGameInfo gi, int jniGamePtr ) CurGameInfo gi, GamePtr jniGamePtr )
{ {
boolean success = false; boolean success = false;
if ( XWApp.REMATCH_SUPPORTED ) { if ( XWApp.REMATCH_SUPPORTED ) {
@ -2656,7 +2657,7 @@ public class BoardDelegate extends DelegateBase
} }
doIt = false; doIt = false;
} else { } else {
CommsAddrRec[] addrs = XwJNI.comms_getAddrs( jniGamePtr ); CommsAddrRec[] addrs = XwJNI.comms_getAddrs( jniGamePtr.ptr() );
for ( int ii = 0; ii < addrs.length; ++ii ) { for ( int ii = 0; ii < addrs.length; ++ii ) {
CommsAddrRec addr = addrs[ii]; CommsAddrRec addr = addrs[ii];
if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) { if ( addr.contains( CommsConnType.COMMS_CONN_BT ) ) {
@ -2669,7 +2670,7 @@ public class BoardDelegate extends DelegateBase
} }
if ( addr.contains( CommsConnType.COMMS_CONN_RELAY ) ) { if ( addr.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
Assert.assertNull( relayID ); Assert.assertNull( relayID );
relayID = XwJNI.comms_formatRelayID( jniGamePtr, ii ); relayID = XwJNI.comms_formatRelayID( jniGamePtr.ptr(), ii );
} }
} }
} }
@ -2696,11 +2697,11 @@ public class BoardDelegate extends DelegateBase
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
GameSummary summary = DBUtils.getSummary( activity, lock ); GameSummary summary = DBUtils.getSummary( activity, lock );
CurGameInfo gi = new CurGameInfo( activity ); CurGameInfo gi = new CurGameInfo( activity );
int gamePtr = GameUtils.loadMakeGame( activity, gi, lock ); GamePtr gamePtr = GameUtils.loadMakeGame( activity, gi, lock );
doRematchIf( activity, null, rowID, summary, gi, gamePtr ); doRematchIf( activity, null, rowID, summary, gi, gamePtr );
XwJNI.game_dispose( gamePtr ); gamePtr.release();
lock.unlock(); lock.unlock();
} else { } else {
DbgUtils.logf( "setupRematchFor(): unable to lock game" ); DbgUtils.logf( "setupRematchFor(): unable to lock game" );

View file

@ -58,7 +58,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
private int m_defaultFontHt; private int m_defaultFontHt;
private int m_mediumFontHt; private int m_mediumFontHt;
private Runnable m_invalidator; private Runnable m_invalidator;
private int m_jniGamePtr; private XwJNI.GamePtr m_jniGamePtr;
private CurGameInfo m_gi; private CurGameInfo m_gi;
private boolean m_isSolo; private boolean m_isSolo;
private int m_layoutWidth; private int m_layoutWidth;
@ -270,7 +270,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
// BoardHandler interface implementation // BoardHandler interface implementation
public void startHandling( Activity parent, JNIThread thread, public void startHandling( Activity parent, JNIThread thread,
int gamePtr, CurGameInfo gi, XwJNI.GamePtr gamePtr, CurGameInfo gi,
CommsConnTypeSet connTypes ) CommsConnTypeSet connTypes )
{ {
m_parent = parent; m_parent = parent;
@ -298,7 +298,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
public void stopHandling() public void stopHandling()
{ {
m_jniThread = null; m_jniThread = null;
m_jniGamePtr = 0; m_jniGamePtr = null;
if ( null != m_canvas ) { if ( null != m_canvas ) {
m_canvas.setJNIThread( null ); m_canvas.setJNIThread( null );
} }
@ -309,7 +309,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
{ {
boolean drew; boolean drew;
synchronized( this ) { synchronized( this ) {
if ( !XwJNI.board_draw( m_jniGamePtr ) ) { if ( !XwJNI.board_draw( m_jniGamePtr.ptr() ) ) {
DbgUtils.logf( "doJNIDraw: draw not complete" ); DbgUtils.logf( "doJNIDraw: draw not complete" );
} }
} }

View file

@ -44,7 +44,6 @@ public class CommsTransport implements TransportProcs,
NetStateCache.StateChangedIf { NetStateCache.StateChangedIf {
private Selector m_selector; private Selector m_selector;
private SocketChannel m_socketChannel; private SocketChannel m_socketChannel;
private int m_jniGamePtr;
private CommsAddrRec m_relayAddr; private CommsAddrRec m_relayAddr;
private String m_useHost; private String m_useHost;
private JNIThread m_jniThread; private JNIThread m_jniThread;
@ -64,11 +63,9 @@ public class CommsTransport implements TransportProcs,
private byte[] m_packetIn; private byte[] m_packetIn;
private int m_haveLen = -1; private int m_haveLen = -1;
public CommsTransport( int jniGamePtr, Context context, public CommsTransport( Context context, TransportProcs.TPMsgHandler handler,
TransportProcs.TPMsgHandler handler,
long rowid, DeviceRole role ) long rowid, DeviceRole role )
{ {
m_jniGamePtr = jniGamePtr;
m_context = context; m_context = context;
m_tpHandler = handler; m_tpHandler = handler;
m_rowid = rowid; m_rowid = rowid;

View file

@ -543,12 +543,12 @@ public class GameConfigDelegate extends DelegateBase
m_giOrig = new CurGameInfo( m_activity ); m_giOrig = new CurGameInfo( m_activity );
GameLock gameLock = new GameLock( m_rowid, false ).lock(); GameLock gameLock = new GameLock( m_rowid, false ).lock();
int gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, gameLock ); XwJNI.GamePtr gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, gameLock );
if ( 0 == gamePtr ) { if ( null == gamePtr ) {
showDictGoneFinish(); showDictGoneFinish();
} else { } else {
m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0 m_gameStarted = XwJNI.model_getNMoves( gamePtr.ptr() ) > 0
|| XwJNI.comms_isConnected( gamePtr ); || XwJNI.comms_isConnected( gamePtr.ptr() );
if ( m_gameStarted ) { if ( m_gameStarted ) {
if ( null == m_gameLockedCheck ) { if ( null == m_gameLockedCheck ) {
@ -565,9 +565,9 @@ public class GameConfigDelegate extends DelegateBase
} }
m_carOrig = new CommsAddrRec(); m_carOrig = new CommsAddrRec();
if ( XwJNI.game_hasComms( gamePtr ) ) { if ( XwJNI.game_hasComms( gamePtr.ptr() ) ) {
XwJNI.comms_getAddr( gamePtr, m_carOrig ); XwJNI.comms_getAddr( gamePtr.ptr(), m_carOrig );
m_remoteAddrs = XwJNI.comms_getAddrs( gamePtr ); m_remoteAddrs = XwJNI.comms_getAddrs( gamePtr.ptr() );
} else if ( !localOnlyGame() ) { } else if ( !localOnlyGame() ) {
String relayName = XWPrefs.getDefaultRelayHost( m_activity ); String relayName = XWPrefs.getDefaultRelayHost( m_activity );
int relayPort = XWPrefs.getDefaultRelayPort( m_activity ); int relayPort = XWPrefs.getDefaultRelayPort( m_activity );
@ -575,7 +575,7 @@ public class GameConfigDelegate extends DelegateBase
relayPort ); relayPort );
} }
m_conTypes = (CommsConnTypeSet)m_carOrig.conTypes.clone(); m_conTypes = (CommsConnTypeSet)m_carOrig.conTypes.clone();
XwJNI.game_dispose( gamePtr ); gamePtr.release();
gameLock.unlock(); gameLock.unlock();

View file

@ -44,11 +44,12 @@ import org.json.JSONObject;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.LastMoveInfo; import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
import org.eehouse.android.xw4.loc.LocUtils;
public class GameUtils { public class GameUtils {
@ -107,23 +108,23 @@ public class GameUtils {
// loadMakeGame, if making a new game, will add comms as long // loadMakeGame, if making a new game, will add comms as long
// as DeviceRole.SERVER_STANDALONE != gi.serverRole // as DeviceRole.SERVER_STANDALONE != gi.serverRole
int gamePtr = loadMakeGame( context, gi, lockSrc ); GamePtr gamePtr = loadMakeGame( context, gi, lockSrc );
String[] dictNames = gi.dictNames(); String[] dictNames = gi.dictNames();
DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames );
if ( XwJNI.game_hasComms( gamePtr ) ) { if ( XwJNI.game_hasComms( gamePtr.ptr() ) ) {
addr = new CommsAddrRec(); addr = new CommsAddrRec();
XwJNI.comms_getAddr( gamePtr, addr ); XwJNI.comms_getAddr( gamePtr.ptr(), addr );
if ( 0 == addr.conTypes.size() ) { if ( 0 == addr.conTypes.size() ) {
String relayName = XWPrefs.getDefaultRelayHost( context ); String relayName = XWPrefs.getDefaultRelayHost( context );
int relayPort = XWPrefs.getDefaultRelayPort( context ); int relayPort = XWPrefs.getDefaultRelayPort( context );
XwJNI.comms_getInitialAddr( addr, relayName, relayPort ); XwJNI.comms_getInitialAddr( addr, relayName, relayPort );
} }
} }
XwJNI.game_dispose( gamePtr ); gamePtr.release();
gamePtr = XwJNI.initJNI(); gamePtr = XwJNI.initJNI();
XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get( context ), XwJNI.game_makeNewGame( gamePtr.ptr(), gi, JNIUtilsImpl.get( context ),
CommonPrefs.get( context ), dictNames, CommonPrefs.get( context ), dictNames,
pairs.m_bytes, pairs.m_paths, gi.langName() ); pairs.m_bytes, pairs.m_paths, gi.langName() );
@ -132,7 +133,7 @@ public class GameUtils {
} }
if ( null != addr ) { if ( null != addr ) {
XwJNI.comms_setAddr( gamePtr, addr ); XwJNI.comms_setAddr( gamePtr.ptr(), addr );
} }
if ( null == lockDest ) { if ( null == lockDest ) {
@ -178,15 +179,16 @@ public class GameUtils {
} }
private static GameSummary summarizeAndClose( Context context, private static GameSummary summarizeAndClose( Context context,
GameLock lock, int gamePtr, GameLock lock,
GamePtr gamePtr,
CurGameInfo gi ) CurGameInfo gi )
{ {
GameSummary summary = new GameSummary( context, gi ); GameSummary summary = new GameSummary( context, gi );
XwJNI.game_summarize( gamePtr, summary ); XwJNI.game_summarize( gamePtr.ptr(), summary );
DBUtils.saveSummary( context, lock, summary ); DBUtils.saveSummary( context, lock, summary );
XwJNI.game_dispose( gamePtr ); gamePtr.release();
return summary; return summary;
} }
@ -194,8 +196,8 @@ public class GameUtils {
{ {
GameSummary result = null; GameSummary result = null;
CurGameInfo gi = new CurGameInfo( context ); CurGameInfo gi = new CurGameInfo( context );
int gamePtr = loadMakeGame( context, gi, lock ); GamePtr gamePtr = loadMakeGame( context, gi, lock );
if ( 0 < gamePtr ) { if ( null != gamePtr ) {
result = summarizeAndClose( context, lock, gamePtr, gi ); result = summarizeAndClose( context, lock, gamePtr, gi );
} }
return result; return result;
@ -266,23 +268,23 @@ public class GameUtils {
return LocUtils.getString( context, R.string.game_fmt, count ); return LocUtils.getString( context, R.string.game_fmt, count );
} }
public static int loadMakeGame( Context context, CurGameInfo gi, public static GamePtr loadMakeGame( Context context, CurGameInfo gi,
TransportProcs tp, GameLock lock ) TransportProcs tp, GameLock lock )
{ {
return loadMakeGame( context, gi, null, tp, lock ); return loadMakeGame( context, gi, null, tp, lock );
} }
public static int loadMakeGame( Context context, CurGameInfo gi, public static GamePtr loadMakeGame( Context context, CurGameInfo gi,
GameLock lock ) GameLock lock )
{ {
return loadMakeGame( context, gi, null, null, lock ); return loadMakeGame( context, gi, null, null, lock );
} }
public static int loadMakeGame( Context context, CurGameInfo gi, public static GamePtr loadMakeGame( Context context, CurGameInfo gi,
UtilCtxt util, TransportProcs tp, UtilCtxt util, TransportProcs tp,
GameLock lock ) GameLock lock )
{ {
int gamePtr = 0; GamePtr gamePtr = null;
byte[] stream = savedGame( context, lock ); byte[] stream = savedGame( context, lock );
if ( null == stream ) { if ( null == stream ) {
@ -299,14 +301,14 @@ public class GameUtils {
String langName = gi.langName(); String langName = gi.langName();
boolean madeGame = boolean madeGame =
XwJNI.game_makeFromStream( gamePtr, stream, gi, XwJNI.game_makeFromStream( gamePtr.ptr(), stream, gi,
dictNames, pairs.m_bytes, dictNames, pairs.m_bytes,
pairs.m_paths, langName, pairs.m_paths, langName,
util, JNIUtilsImpl.get( context ), util, JNIUtilsImpl.get( context ),
CommonPrefs.get(context), CommonPrefs.get(context),
tp); tp);
if ( !madeGame ) { if ( !madeGame ) {
XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(context), XwJNI.game_makeNewGame( gamePtr.ptr(), gi, JNIUtilsImpl.get(context),
CommonPrefs.get(context), dictNames, CommonPrefs.get(context), dictNames,
pairs.m_bytes, pairs.m_paths, pairs.m_bytes, pairs.m_paths,
langName ); langName );
@ -322,19 +324,18 @@ public class GameUtils {
GameLock lock = new GameLock( rowid, false ); GameLock lock = new GameLock( rowid, false );
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
CurGameInfo gi = new CurGameInfo( activity ); CurGameInfo gi = new CurGameInfo( activity );
int gamePtr = loadMakeGame( activity, gi, lock ); GamePtr gamePtr = loadMakeGame( activity, gi, lock );
if ( 0 != gamePtr ) { if ( null != gamePtr ) {
thumb = takeSnapshot( activity, gamePtr, gi ); thumb = takeSnapshot( activity, gamePtr, gi );
XwJNI.game_dispose( gamePtr ); gamePtr.release();
DBUtils.saveThumbnail( activity, lock, thumb ); DBUtils.saveThumbnail( activity, lock, thumb );
} }
lock.unlock(); lock.unlock();
} }
return thumb; return thumb;
} }
public static Bitmap takeSnapshot( Context context, int gamePtr, public static Bitmap takeSnapshot( Context context, GamePtr gamePtr,
CurGameInfo gi ) CurGameInfo gi )
{ {
Bitmap thumb = null; Bitmap thumb = null;
@ -361,13 +362,13 @@ public class GameUtils {
thumb = Bitmap.createBitmap( size, size, thumb = Bitmap.createBitmap( size, size,
Bitmap.Config.ARGB_8888 ); Bitmap.Config.ARGB_8888 );
XwJNI.board_figureLayout( gamePtr, gi, 0, 0, size, size, XwJNI.board_figureLayout( gamePtr.ptr(), gi, 0, 0, size, size,
0, 0, 0, 20, 20, false, null ); 0, 0, 0, 20, 20, false, null );
ThumbCanvas canvas = new ThumbCanvas( context, thumb ); ThumbCanvas canvas = new ThumbCanvas( context, thumb );
XwJNI.board_setDraw( gamePtr, canvas ); XwJNI.board_setDraw( gamePtr.ptr(), canvas );
XwJNI.board_invalAll( gamePtr ); XwJNI.board_invalAll( gamePtr.ptr() );
XwJNI.board_draw( gamePtr ); XwJNI.board_draw( gamePtr.ptr() );
} }
} }
} }
@ -400,18 +401,18 @@ public class GameUtils {
} }
} }
public static long saveGame( Context context, int gamePtr, public static long saveGame( Context context, GamePtr gamePtr,
CurGameInfo gi, GameLock lock, CurGameInfo gi, GameLock lock,
boolean setCreate ) boolean setCreate )
{ {
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); byte[] stream = XwJNI.game_saveToStream( gamePtr.ptr(), gi );
return saveGame( context, stream, lock, setCreate ); return saveGame( context, stream, lock, setCreate );
} }
public static long saveNewGame( Context context, int gamePtr, public static long saveNewGame( Context context, GamePtr gamePtr,
CurGameInfo gi, long groupID ) CurGameInfo gi, long groupID )
{ {
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi ); byte[] stream = XwJNI.game_saveToStream( gamePtr.ptr(), gi );
GameLock lock = DBUtils.saveNewGame( context, stream, groupID, null ); GameLock lock = DBUtils.saveNewGame( context, stream, groupID, null );
long rowid = lock.getRowid(); long rowid = lock.getRowid();
lock.unlock(); lock.unlock();
@ -890,19 +891,19 @@ public class GameUtils {
if ( null != lock ) { if ( null != lock ) {
CurGameInfo gi = new CurGameInfo( context ); CurGameInfo gi = new CurGameInfo( context );
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock ); GamePtr gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
if ( 0 != gamePtr ) { if ( null != gamePtr ) {
XwJNI.comms_resendAll( gamePtr, false, false ); XwJNI.comms_resendAll( gamePtr.ptr(), false, false );
for ( byte[] msg : msgs ) { for ( byte[] msg : msgs ) {
Assert.assertNotNull( ret ); Assert.assertNotNull( ret );
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret ) draw = XwJNI.game_receiveMessage( gamePtr.ptr(), msg, ret )
|| draw; || draw;
} }
XwJNI.comms_ackAny( gamePtr ); XwJNI.comms_ackAny( gamePtr.ptr() );
// update gi to reflect changes due to messages // update gi to reflect changes due to messages
XwJNI.game_getGi( gamePtr, gi ); XwJNI.game_getGi( gamePtr.ptr(), gi );
if ( draw && XWPrefs.getThumbEnabled( context ) ) { if ( draw && XWPrefs.getThumbEnabled( context ) ) {
Bitmap bitmap = takeSnapshot( context, gamePtr, gi ); Bitmap bitmap = takeSnapshot( context, gamePtr, gi );
@ -915,7 +916,7 @@ public class GameUtils {
bmr.m_chatFrom = feedImpl.m_chatFrom; bmr.m_chatFrom = feedImpl.m_chatFrom;
} else { } else {
LastMoveInfo lmi = new LastMoveInfo(); LastMoveInfo lmi = new LastMoveInfo();
XwJNI.model_getPlayersLastScore( gamePtr, -1, lmi ); XwJNI.model_getPlayersLastScore( gamePtr.ptr(), -1, lmi );
bmr.m_lmi = lmi; bmr.m_lmi = lmi;
} }
} }
@ -971,8 +972,8 @@ public class GameUtils {
DictUtils.DictPairs pairs = DictUtils.openDicts( context, DictUtils.DictPairs pairs = DictUtils.openDicts( context,
dictNames ); dictNames );
int gamePtr = XwJNI.initJNI( rowid ); GamePtr gamePtr = XwJNI.initJNI( rowid );
XwJNI.game_makeFromStream( gamePtr, stream, gi, dictNames, XwJNI.game_makeFromStream( gamePtr.ptr(), stream, gi, dictNames,
pairs.m_bytes, pairs.m_paths, pairs.m_bytes, pairs.m_paths,
gi.langName(), gi.langName(),
JNIUtilsImpl.get(context), JNIUtilsImpl.get(context),
@ -1011,7 +1012,7 @@ public class GameUtils {
String[] dictNames = gi.dictNames(); String[] dictNames = gi.dictNames();
DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames ); DictUtils.DictPairs pairs = DictUtils.openDicts( context, dictNames );
String langName = gi.langName(); String langName = gi.langName();
int gamePtr = XwJNI.initJNI( lock.getRowid() ); GamePtr gamePtr = XwJNI.initJNI( lock.getRowid() );
boolean madeGame = false; boolean madeGame = false;
CommonPrefs cp = CommonPrefs.get( context ); CommonPrefs cp = CommonPrefs.get( context );
@ -1020,7 +1021,7 @@ public class GameUtils {
} else { } else {
byte[] stream = savedGame( context, lock ); byte[] stream = savedGame( context, lock );
// Will fail if there's nothing in the stream but a gi. // Will fail if there's nothing in the stream but a gi.
madeGame = XwJNI.game_makeFromStream( gamePtr, stream, madeGame = XwJNI.game_makeFromStream( gamePtr.ptr(), stream,
new CurGameInfo(context), new CurGameInfo(context),
dictNames, pairs.m_bytes, dictNames, pairs.m_bytes,
pairs.m_paths, langName, pairs.m_paths, langName,
@ -1029,7 +1030,7 @@ public class GameUtils {
} }
if ( forceNew || !madeGame ) { if ( forceNew || !madeGame ) {
XwJNI.game_makeNewGame( gamePtr, gi, util, XwJNI.game_makeNewGame( gamePtr.ptr(), gi, util,
JNIUtilsImpl.get(context), JNIUtilsImpl.get(context),
(DrawCtx)null, (DrawCtx)null,
cp, sink, dictNames, pairs.m_bytes, cp, sink, dictNames, pairs.m_bytes,
@ -1037,20 +1038,19 @@ public class GameUtils {
} }
if ( null != car ) { if ( null != car ) {
XwJNI.comms_setAddr( gamePtr, car ); XwJNI.comms_setAddr( gamePtr.ptr(), car );
} }
if ( null != sink ) { if ( null != sink ) {
JNIThread.tryConnectClient( gamePtr, gi ); JNIThread.tryConnectClient( gamePtr.ptr(), gi );
} }
saveGame( context, gamePtr, gi, lock, false ); saveGame( context, gamePtr, gi, lock, false );
GameSummary summary = new GameSummary( context, gi ); GameSummary summary = new GameSummary( context, gi );
XwJNI.game_summarize( gamePtr, summary ); XwJNI.game_summarize( gamePtr.ptr(), summary );
gamePtr.release();
DBUtils.saveSummary( context, lock, summary ); DBUtils.saveSummary( context, lock, summary );
XwJNI.game_dispose( gamePtr );
} // applyChanges } // applyChanges
public static void doConfig( Activity activity, long rowid, Class clazz ) public static void doConfig( Activity activity, long rowid, Class clazz )
@ -1216,9 +1216,10 @@ public class GameUtils {
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
CurGameInfo gi = new CurGameInfo( m_context ); CurGameInfo gi = new CurGameInfo( m_context );
m_sink = new MultiMsgSink( m_context, rowid ); m_sink = new MultiMsgSink( m_context, rowid );
int gamePtr = loadMakeGame( m_context, gi, m_sink, lock ); GamePtr gamePtr = loadMakeGame( m_context, gi, m_sink, lock );
if ( 0 != gamePtr ) { if ( null != gamePtr ) {
XwJNI.comms_resendAll( gamePtr, true, false ); XwJNI.comms_resendAll( gamePtr.ptr(), true, false );
gamePtr.release();
} }
lock.unlock(); lock.unlock();
} else { } else {

View file

@ -26,7 +26,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
public interface BoardHandler { public interface BoardHandler {
void startHandling( Activity parent, JNIThread thread, void startHandling( Activity parent, JNIThread thread,
int gamePtr, CurGameInfo gi, XwJNI.GamePtr gamePtr, CurGameInfo gi,
CommsConnTypeSet connTypes ); CommsConnTypeSet connTypes );
} }

View file

@ -43,6 +43,7 @@ import org.eehouse.android.xw4.XWPrefs;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.DrawCtx; import org.eehouse.android.xw4.jni.DrawCtx;
import org.eehouse.android.xw4.jni.XwJNI.GamePtr;
import junit.framework.Assert; import junit.framework.Assert;
@ -132,7 +133,7 @@ public class JNIThread extends Thread {
private boolean m_stopped = false; private boolean m_stopped = false;
private boolean m_saveOnStop = false; private boolean m_saveOnStop = false;
private int m_jniGamePtr; private GamePtr m_jniGamePtr;
private byte[] m_gameAtStart; private byte[] m_gameAtStart;
private GameLock m_lock; private GameLock m_lock;
private Context m_context; private Context m_context;
@ -155,7 +156,7 @@ public class JNIThread extends Thread {
Object[] m_args; Object[] m_args;
} }
public JNIThread( int gamePtr, byte[] gameAtStart, CurGameInfo gi, public JNIThread( GamePtr gamePtr, byte[] gameAtStart, CurGameInfo gi,
SyncedDraw drawer, GameLock lock, Context context, SyncedDraw drawer, GameLock lock, Context context,
Handler handler ) Handler handler )
{ {
@ -219,11 +220,11 @@ public class JNIThread extends Thread {
private boolean toggleTray() { private boolean toggleTray() {
boolean draw; boolean draw;
int state = XwJNI.board_getTrayVisState( m_jniGamePtr ); int state = XwJNI.board_getTrayVisState( m_jniGamePtr.ptr() );
if ( state == XwJNI.TRAY_REVEALED ) { if ( state == XwJNI.TRAY_REVEALED ) {
draw = XwJNI.board_hideTray( m_jniGamePtr ); draw = XwJNI.board_hideTray( m_jniGamePtr.ptr() );
} else { } else {
draw = XwJNI.board_showTray( m_jniGamePtr ); draw = XwJNI.board_showTray( m_jniGamePtr.ptr() );
} }
return draw; return draw;
} }
@ -239,7 +240,7 @@ public class JNIThread extends Thread {
BoardDims dims = new BoardDims(); BoardDims dims = new BoardDims();
boolean squareTiles = XWPrefs.getSquareTiles( m_context ); boolean squareTiles = XWPrefs.getSquareTiles( m_context );
XwJNI.board_figureLayout( m_jniGamePtr, m_gi, 0, 0, width, height, XwJNI.board_figureLayout( m_jniGamePtr.ptr(), m_gi, 0, 0, width, height,
150 /*scorePct*/, 200 /*trayPct*/, 150 /*scorePct*/, 200 /*trayPct*/,
width, fontWidth, fontHeight, squareTiles, width, fontWidth, fontHeight, squareTiles,
dims /* out param */ ); dims /* out param */ );
@ -255,7 +256,7 @@ public class JNIThread extends Thread {
ConnStatusHandler.clearRect(); ConnStatusHandler.clearRect();
} }
XwJNI.board_applyLayout( m_jniGamePtr, dims ); XwJNI.board_applyLayout( m_jniGamePtr.ptr(), dims );
m_drawer.dimsChanged( dims ); m_drawer.dimsChanged( dims );
} }
@ -276,7 +277,7 @@ public class JNIThread extends Thread {
private void checkButtons() private void checkButtons()
{ {
synchronized( m_gsi ) { synchronized( m_gsi ) {
XwJNI.game_getState( m_jniGamePtr, m_gsi ); XwJNI.game_getState( m_jniGamePtr.ptr(), m_gsi );
} }
Message.obtain( m_handler, TOOLBAR_STATES ).sendToTarget(); Message.obtain( m_handler, TOOLBAR_STATES ).sendToTarget();
} }
@ -286,27 +287,27 @@ public class JNIThread extends Thread {
// If server has any work to do, e.g. clean up after showing a // If server has any work to do, e.g. clean up after showing a
// remote- or robot-moved dialog, let it do so before saving // remote- or robot-moved dialog, let it do so before saving
// state. In some cases it'll otherwise drop the move. // state. In some cases it'll otherwise drop the move.
XwJNI.server_do( m_jniGamePtr ); XwJNI.server_do( m_jniGamePtr.ptr() );
// And let it tell the relay (if any) it's leaving // And let it tell the relay (if any) it's leaving
// XwJNI.comms_stop( m_jniGamePtr ); // XwJNI.comms_stop( m_jniGamePtr );
XwJNI.game_getGi( m_jniGamePtr, m_gi ); XwJNI.game_getGi( m_jniGamePtr.ptr(), m_gi );
if ( null != m_newDict ) { if ( null != m_newDict ) {
m_gi.dictName = m_newDict; m_gi.dictName = m_newDict;
} }
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi ); byte[] state = XwJNI.game_saveToStream( m_jniGamePtr.ptr(), m_gi );
if ( Arrays.equals( m_gameAtStart, state ) ) { if ( Arrays.equals( m_gameAtStart, state ) ) {
// DbgUtils.logf( "no change in game; can skip saving" ); // DbgUtils.logf( "no change in game; can skip saving" );
} else { } else {
synchronized( this ) { synchronized( this ) {
Assert.assertNotNull( m_lock ); Assert.assertNotNull( m_lock );
GameSummary summary = new GameSummary( m_context, m_gi ); GameSummary summary = new GameSummary( m_context, m_gi );
XwJNI.game_summarize( m_jniGamePtr, summary ); XwJNI.game_summarize( m_jniGamePtr.ptr(), summary );
DBUtils.saveGame( m_context, m_lock, state, false ); DBUtils.saveGame( m_context, m_lock, state, false );
DBUtils.saveSummary( m_context, m_lock, summary ); DBUtils.saveSummary( m_context, m_lock, summary );
// There'd better be no way for saveGame above to fail! // There'd better be no way for saveGame above to fail!
XwJNI.game_saveSucceeded( m_jniGamePtr ); XwJNI.game_saveSucceeded( m_jniGamePtr.ptr() );
} }
} }
} }
@ -354,12 +355,12 @@ public class JNIThread extends Thread {
break; break;
case CMD_SETDRAW: case CMD_SETDRAW:
XwJNI.board_setDraw( m_jniGamePtr, (DrawCtx)args[0] ); XwJNI.board_setDraw( m_jniGamePtr.ptr(), (DrawCtx)args[0] );
XwJNI.board_invalAll( m_jniGamePtr ); XwJNI.board_invalAll( m_jniGamePtr.ptr() );
break; break;
case CMD_INVALALL: case CMD_INVALALL:
XwJNI.board_invalAll( m_jniGamePtr ); XwJNI.board_invalAll( m_jniGamePtr.ptr() );
draw = true; draw = true;
break; break;
@ -368,7 +369,7 @@ public class JNIThread extends Thread {
BoardDims dims = null; BoardDims dims = null;
if ( args0 instanceof BoardDims ) { if ( args0 instanceof BoardDims ) {
dims = (BoardDims)args0; dims = (BoardDims)args0;
XwJNI.board_applyLayout( m_jniGamePtr, dims ); XwJNI.board_applyLayout( m_jniGamePtr.ptr(), dims );
} else { } else {
doLayout( (Integer)args0, (Integer)args[1], doLayout( (Integer)args0, (Integer)args[1],
(Integer)args[2], (Integer)args[3] ); (Integer)args[2], (Integer)args[3] );
@ -379,29 +380,29 @@ public class JNIThread extends Thread {
break; break;
case CMD_RESET: case CMD_RESET:
XwJNI.comms_resetSame( m_jniGamePtr ); XwJNI.comms_resetSame( m_jniGamePtr.ptr() );
// FALLTHRU // FALLTHRU
case CMD_START: case CMD_START:
draw = tryConnectClient( m_jniGamePtr, m_gi ); draw = tryConnectClient( m_jniGamePtr.ptr(), m_gi );
break; break;
case CMD_SWITCHCLIENT: case CMD_SWITCHCLIENT:
XwJNI.server_reset( m_jniGamePtr ); XwJNI.server_reset( m_jniGamePtr.ptr() );
XwJNI.server_initClientConnection( m_jniGamePtr ); XwJNI.server_initClientConnection( m_jniGamePtr.ptr() );
draw = XwJNI.server_do( m_jniGamePtr ); draw = XwJNI.server_do( m_jniGamePtr.ptr() );
break; break;
case CMD_DO: case CMD_DO:
if ( nextSame( JNICmd.CMD_DO ) ) { if ( nextSame( JNICmd.CMD_DO ) ) {
continue; continue;
} }
draw = XwJNI.server_do( m_jniGamePtr ); draw = XwJNI.server_do( m_jniGamePtr.ptr() );
break; break;
case CMD_RECEIVE: case CMD_RECEIVE:
CommsAddrRec ret = (CommsAddrRec)args[1]; CommsAddrRec ret = (CommsAddrRec)args[1];
Assert.assertNotNull( ret ); Assert.assertNotNull( ret );
draw = XwJNI.game_receiveMessage( m_jniGamePtr, draw = XwJNI.game_receiveMessage( m_jniGamePtr.ptr(),
(byte[])args[0], ret ); (byte[])args[0], ret );
handle( JNICmd.CMD_DO ); handle( JNICmd.CMD_DO );
if ( draw ) { if ( draw ) {
@ -411,21 +412,21 @@ public class JNIThread extends Thread {
case CMD_TRANSFAIL: case CMD_TRANSFAIL:
CommsConnType typ = (CommsConnType)args[0]; CommsConnType typ = (CommsConnType)args[0];
XwJNI.comms_transportFailed( m_jniGamePtr, typ ); XwJNI.comms_transportFailed( m_jniGamePtr.ptr(), typ );
break; break;
case CMD_PREFS_CHANGE: case CMD_PREFS_CHANGE:
// need to inval all because some of prefs, // need to inval all because some of prefs,
// e.g. colors, aren't known by common code so // e.g. colors, aren't known by common code so
// board_prefsChanged's return value isn't enough. // board_prefsChanged's return value isn't enough.
XwJNI.board_invalAll( m_jniGamePtr ); XwJNI.board_invalAll( m_jniGamePtr.ptr() );
XwJNI.board_server_prefsChanged( m_jniGamePtr, XwJNI.board_server_prefsChanged( m_jniGamePtr.ptr(),
CommonPrefs.get( m_context ) ); CommonPrefs.get( m_context ) );
draw = true; draw = true;
break; break;
case CMD_PEN_DOWN: case CMD_PEN_DOWN:
draw = XwJNI.board_handlePenDown( m_jniGamePtr, draw = XwJNI.board_handlePenDown( m_jniGamePtr.ptr(),
((Integer)args[0]).intValue(), ((Integer)args[0]).intValue(),
((Integer)args[1]).intValue(), ((Integer)args[1]).intValue(),
barr ); barr );
@ -434,12 +435,12 @@ public class JNIThread extends Thread {
if ( nextSame( JNICmd.CMD_PEN_MOVE ) ) { if ( nextSame( JNICmd.CMD_PEN_MOVE ) ) {
continue; continue;
} }
draw = XwJNI.board_handlePenMove( m_jniGamePtr, draw = XwJNI.board_handlePenMove( m_jniGamePtr.ptr(),
((Integer)args[0]).intValue(), ((Integer)args[0]).intValue(),
((Integer)args[1]).intValue() ); ((Integer)args[1]).intValue() );
break; break;
case CMD_PEN_UP: case CMD_PEN_UP:
draw = XwJNI.board_handlePenUp( m_jniGamePtr, draw = XwJNI.board_handlePenUp( m_jniGamePtr.ptr(),
((Integer)args[0]).intValue(), ((Integer)args[0]).intValue(),
((Integer)args[1]).intValue() ); ((Integer)args[1]).intValue() );
break; break;
@ -450,30 +451,30 @@ public class JNIThread extends Thread {
break; break;
case CMD_COMMIT: case CMD_COMMIT:
draw = XwJNI.board_commitTurn( m_jniGamePtr ); draw = XwJNI.board_commitTurn( m_jniGamePtr.ptr() );
break; break;
case CMD_JUGGLE: case CMD_JUGGLE:
draw = XwJNI.board_juggleTray( m_jniGamePtr ); draw = XwJNI.board_juggleTray( m_jniGamePtr.ptr() );
break; break;
case CMD_FLIP: case CMD_FLIP:
draw = XwJNI.board_flip( m_jniGamePtr ); draw = XwJNI.board_flip( m_jniGamePtr.ptr() );
break; break;
case CMD_TOGGLE_TRAY: case CMD_TOGGLE_TRAY:
draw = toggleTray(); draw = toggleTray();
break; break;
case CMD_TRADE: case CMD_TRADE:
draw = XwJNI.board_beginTrade( m_jniGamePtr ); draw = XwJNI.board_beginTrade( m_jniGamePtr.ptr() );
break; break;
case CMD_CANCELTRADE: case CMD_CANCELTRADE:
draw = XwJNI.board_endTrade( m_jniGamePtr ); draw = XwJNI.board_endTrade( m_jniGamePtr.ptr() );
break; break;
case CMD_UNDO_CUR: case CMD_UNDO_CUR:
draw = XwJNI.board_replaceTiles( m_jniGamePtr ) draw = XwJNI.board_replaceTiles( m_jniGamePtr.ptr() )
|| XwJNI.board_redoReplacedTiles( m_jniGamePtr ); || XwJNI.board_redoReplacedTiles( m_jniGamePtr.ptr() );
break; break;
case CMD_UNDO_LAST: case CMD_UNDO_LAST:
XwJNI.server_handleUndo( m_jniGamePtr ); XwJNI.server_handleUndo( m_jniGamePtr.ptr() );
draw = true; draw = true;
break; break;
@ -482,7 +483,7 @@ public class JNIThread extends Thread {
if ( nextSame( elem.m_cmd ) ) { if ( nextSame( elem.m_cmd ) ) {
continue; continue;
} }
draw = XwJNI.board_requestHint( m_jniGamePtr, false, draw = XwJNI.board_requestHint( m_jniGamePtr.ptr(), false,
JNICmd.CMD_PREV_HINT==elem.m_cmd, JNICmd.CMD_PREV_HINT==elem.m_cmd,
barr ); barr );
if ( barr[0] ) { if ( barr[0] ) {
@ -492,39 +493,39 @@ public class JNIThread extends Thread {
break; break;
case CMD_TOGGLEZOOM: case CMD_TOGGLEZOOM:
XwJNI.board_zoom( m_jniGamePtr, 0 , barr ); XwJNI.board_zoom( m_jniGamePtr.ptr(), 0 , barr );
int zoomBy = 0; int zoomBy = 0;
if ( barr[1] ) { // always go out if possible if ( barr[1] ) { // always go out if possible
zoomBy = -5; zoomBy = -5;
} else if ( barr[0] ) { } else if ( barr[0] ) {
zoomBy = 5; zoomBy = 5;
} }
draw = XwJNI.board_zoom( m_jniGamePtr, zoomBy, barr ); draw = XwJNI.board_zoom( m_jniGamePtr.ptr(), zoomBy, barr );
break; break;
case CMD_ZOOM: case CMD_ZOOM:
draw = XwJNI.board_zoom( m_jniGamePtr, draw = XwJNI.board_zoom( m_jniGamePtr.ptr(),
((Integer)args[0]).intValue(), ((Integer)args[0]).intValue(),
barr ); barr );
break; break;
case CMD_VALUES: case CMD_VALUES:
draw = XwJNI.board_toggle_showValues( m_jniGamePtr ); draw = XwJNI.board_toggle_showValues( m_jniGamePtr.ptr() );
break; break;
case CMD_COUNTS_VALUES: case CMD_COUNTS_VALUES:
sendForDialog( ((Integer)args[0]).intValue(), sendForDialog( ((Integer)args[0]).intValue(),
XwJNI.server_formatDictCounts( m_jniGamePtr, 3 ) XwJNI.server_formatDictCounts( m_jniGamePtr.ptr(), 3 )
); );
break; break;
case CMD_REMAINING: case CMD_REMAINING:
sendForDialog( ((Integer)args[0]).intValue(), sendForDialog( ((Integer)args[0]).intValue(),
XwJNI.board_formatRemainingTiles( m_jniGamePtr ) XwJNI.board_formatRemainingTiles( m_jniGamePtr.ptr() )
); );
break; break;
case CMD_RESEND: case CMD_RESEND:
int nSent = int nSent =
XwJNI.comms_resendAll( m_jniGamePtr, XwJNI.comms_resendAll( m_jniGamePtr.ptr(),
((Boolean)args[0]).booleanValue(), ((Boolean)args[0]).booleanValue(),
((Boolean)args[1]).booleanValue() ); ((Boolean)args[1]).booleanValue() );
if ( ((Boolean)args[2]).booleanValue() ) { if ( ((Boolean)args[2]).booleanValue() ) {
@ -536,14 +537,14 @@ public class JNIThread extends Thread {
// break; // break;
case CMD_HISTORY: case CMD_HISTORY:
boolean gameOver = XwJNI.server_getGameIsOver( m_jniGamePtr ); boolean gameOver = XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() );
sendForDialog( ((Integer)args[0]).intValue(), sendForDialog( ((Integer)args[0]).intValue(),
XwJNI.model_writeGameHistory( m_jniGamePtr, XwJNI.model_writeGameHistory( m_jniGamePtr.ptr(),
gameOver ) ); gameOver ) );
break; break;
case CMD_FINAL: case CMD_FINAL:
if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) { if ( XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() ) ) {
handle( JNICmd.CMD_POST_OVER ); handle( JNICmd.CMD_POST_OVER );
} else { } else {
Message.obtain( m_handler, QUERY_ENDGAME ).sendToTarget(); Message.obtain( m_handler, QUERY_ENDGAME ).sendToTarget();
@ -551,35 +552,35 @@ public class JNIThread extends Thread {
break; break;
case CMD_ENDGAME: case CMD_ENDGAME:
XwJNI.server_endGame( m_jniGamePtr ); XwJNI.server_endGame( m_jniGamePtr.ptr() );
draw = true; draw = true;
break; break;
case CMD_POST_OVER: case CMD_POST_OVER:
if ( XwJNI.server_getGameIsOver( m_jniGamePtr ) ) { if ( XwJNI.server_getGameIsOver( m_jniGamePtr.ptr() ) ) {
boolean auto = 0 < args.length && boolean auto = 0 < args.length &&
((Boolean)args[0]).booleanValue(); ((Boolean)args[0]).booleanValue();
int titleID = auto? R.string.summary_gameover int titleID = auto? R.string.summary_gameover
: R.string.finalscores_title; : R.string.finalscores_title;
String text = XwJNI.server_writeFinalScores( m_jniGamePtr ); String text = XwJNI.server_writeFinalScores( m_jniGamePtr.ptr() );
Message.obtain( m_handler, GAME_OVER, titleID, 0, text ) Message.obtain( m_handler, GAME_OVER, titleID, 0, text )
.sendToTarget(); .sendToTarget();
} }
break; break;
case CMD_SENDCHAT: case CMD_SENDCHAT:
XwJNI.board_sendChat( m_jniGamePtr, (String)args[0] ); XwJNI.board_sendChat( m_jniGamePtr.ptr(), (String)args[0] );
break; break;
case CMD_NETSTATS: case CMD_NETSTATS:
sendForDialog( ((Integer)args[0]).intValue(), sendForDialog( ((Integer)args[0]).intValue(),
XwJNI.comms_getStats( m_jniGamePtr ) XwJNI.comms_getStats( m_jniGamePtr.ptr() )
); );
break; break;
case CMD_TIMER_FIRED: case CMD_TIMER_FIRED:
draw = XwJNI.timerFired( m_jniGamePtr, draw = XwJNI.timerFired( m_jniGamePtr.ptr(),
((Integer)args[0]).intValue(), ((Integer)args[0]).intValue(),
((Integer)args[1]).intValue(), ((Integer)args[1]).intValue(),
((Integer)args[2]).intValue() ); ((Integer)args[2]).intValue() );
@ -603,7 +604,7 @@ public class JNIThread extends Thread {
} // for } // for
if ( m_saveOnStop ) { if ( m_saveOnStop ) {
XwJNI.comms_stop( m_jniGamePtr ); XwJNI.comms_stop( m_jniGamePtr.ptr() );
save_jni(); save_jni();
} else { } else {
DbgUtils.logf( "JNIThread.run(): exiting without saving" ); DbgUtils.logf( "JNIThread.run(): exiting without saving" );

View file

@ -22,6 +22,8 @@ package org.eehouse.android.xw4.jni;
import android.graphics.Rect; import android.graphics.Rect;
import junit.framework.Assert;
import org.eehouse.android.xw4.DbgUtils; import org.eehouse.android.xw4.DbgUtils;
import org.eehouse.android.xw4.NetLaunchInfo; import org.eehouse.android.xw4.NetLaunchInfo;
import org.eehouse.android.xw4.Utils; import org.eehouse.android.xw4.Utils;
@ -30,6 +32,31 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
// Collection of native methods and a bit of state // Collection of native methods and a bit of state
public class XwJNI { public class XwJNI {
public static class GamePtr {
private int m_ptr = 0;
private GamePtr( int ptr ) { m_ptr = ptr; }
public int ptr() { Assert.assertTrue( 0 != m_ptr ); return m_ptr; }
// Force (via an assert in finalize() below) that this is called. It's
// better if jni stuff isn't being done on the finalizer thread
public void release()
{
if ( 0 != m_ptr ) {
game_dispose( ptr() );
m_ptr = 0;
}
}
// @Override
public void finalize() throws java.lang.Throwable
{
Assert.assertTrue( 0 == m_ptr );
super.finalize();
}
}
private static XwJNI s_JNI = null; private static XwJNI s_JNI = null;
private static XwJNI getJNI() private static XwJNI getJNI()
{ {
@ -102,14 +129,16 @@ public class XwJNI {
public static native String comms_getUUID(); public static native String comms_getUUID();
// Game methods // Game methods
public static int initJNI( long rowid ) public static GamePtr initJNI( long rowid )
{ {
int seed = Utils.nextRandomInt(); int seed = Utils.nextRandomInt();
String tag = String.format( "%d", rowid ); String tag = String.format( "%d", rowid );
return initJNI( getJNI().m_ptr, seed, tag ); int ptr = initJNI( getJNI().m_ptr, seed, tag );
GamePtr result = 0 == ptr ? null : new GamePtr( ptr );
return result;
} }
public static int initJNI() public static GamePtr initJNI()
{ {
return initJNI( 0 ); return initJNI( 0 );
} }
@ -214,7 +243,7 @@ public class XwJNI {
// String dictName, // String dictName,
// byte[] dictBytes, // byte[] dictBytes,
// String dictPath ); // String dictPath );
public static native void game_dispose( int gamePtr ); private static native void game_dispose( int gamePtr );
// Board methods // Board methods
public static native void board_setDraw( int gamePtr, DrawCtx draw ); public static native void board_setDraw( int gamePtr, DrawCtx draw );