mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-05 20:45:49 +01:00
add class that maintains read and write locks per-game record to
prevent writes to a game while it's open. Seems to work, but needs more testing.
This commit is contained in:
parent
4eb77809fb
commit
ce6eca3fe5
8 changed files with 289 additions and 132 deletions
|
@ -68,6 +68,7 @@ public class BoardActivity extends XWActivity
|
||||||
|
|
||||||
private BoardView m_view;
|
private BoardView m_view;
|
||||||
private int m_jniGamePtr;
|
private int m_jniGamePtr;
|
||||||
|
private DBUtils.GameLock m_gameLock;
|
||||||
private CurGameInfo m_gi;
|
private CurGameInfo m_gi;
|
||||||
CommsTransport m_xport;
|
CommsTransport m_xport;
|
||||||
private Handler m_handler;
|
private Handler m_handler;
|
||||||
|
@ -290,38 +291,19 @@ public class BoardActivity extends XWActivity
|
||||||
}
|
}
|
||||||
} // onCreate
|
} // onCreate
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart()
|
|
||||||
{
|
|
||||||
loadGame();
|
|
||||||
super.onStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause()
|
protected void onPause()
|
||||||
{
|
{
|
||||||
if ( null != m_jniThread ) {
|
waitCloseGame( true );
|
||||||
m_jniThread.setInBackground( true );
|
|
||||||
}
|
|
||||||
m_isVisible = false;
|
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume()
|
protected void onResume()
|
||||||
{
|
{
|
||||||
if ( null != m_jniThread ) {
|
|
||||||
m_jniThread.setInBackground( false );
|
|
||||||
}
|
|
||||||
m_isVisible = true;
|
|
||||||
super.onResume();
|
super.onResume();
|
||||||
}
|
loadGame();
|
||||||
|
super.onResume();
|
||||||
@Override
|
|
||||||
protected void onDestroy()
|
|
||||||
{
|
|
||||||
waitCloseGame( true );
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1070,7 +1052,10 @@ public class BoardActivity extends XWActivity
|
||||||
private void loadGame()
|
private void loadGame()
|
||||||
{
|
{
|
||||||
if ( 0 == m_jniGamePtr ) {
|
if ( 0 == m_jniGamePtr ) {
|
||||||
byte[] stream = GameUtils.savedGame( this, m_path );
|
Assert.assertNull( m_gameLock );
|
||||||
|
m_gameLock = new DBUtils.GameLock( m_path, true ).lock();
|
||||||
|
|
||||||
|
byte[] stream = GameUtils.savedGame( this, m_gameLock );
|
||||||
XwJNI.gi_from_stream( m_gi, stream );
|
XwJNI.gi_from_stream( m_gi, stream );
|
||||||
|
|
||||||
Utils.logf( "loadGame: dict name: %s", m_gi.dictName );
|
Utils.logf( "loadGame: dict name: %s", m_gi.dictName );
|
||||||
|
@ -1095,7 +1080,7 @@ public class BoardActivity extends XWActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
m_jniThread = new
|
m_jniThread = new
|
||||||
JNIThread( m_jniGamePtr, m_gi, m_view, m_path, this,
|
JNIThread( m_jniGamePtr, m_gi, m_view, m_gameLock, this,
|
||||||
new Handler() {
|
new Handler() {
|
||||||
public void handleMessage( Message msg ) {
|
public void handleMessage( Message msg ) {
|
||||||
switch( msg.what ) {
|
switch( msg.what ) {
|
||||||
|
@ -1313,6 +1298,9 @@ public class BoardActivity extends XWActivity
|
||||||
|
|
||||||
XwJNI.game_dispose( m_jniGamePtr );
|
XwJNI.game_dispose( m_jniGamePtr );
|
||||||
m_jniGamePtr = 0;
|
m_jniGamePtr = 0;
|
||||||
|
|
||||||
|
m_gameLock.unlock();
|
||||||
|
m_gameLock = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,12 @@ import java.util.StringTokenizer;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.jni.*;
|
import org.eehouse.android.xw4.jni.*;
|
||||||
|
@ -63,7 +67,88 @@ public class DBUtils {
|
||||||
boolean sourceLocal;
|
boolean sourceLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Implements read-locks and write-locks per game. A read lock is
|
||||||
|
// obtainable when other read locks are granted but not when a
|
||||||
|
// write lock is. Write-locks are exclusive.
|
||||||
|
public static class GameLock {
|
||||||
|
private String m_path;
|
||||||
|
private boolean m_isForWrite;
|
||||||
|
private ReentrantReadWriteLock m_rwlock;
|
||||||
|
|
||||||
|
// This will leak empty ReentrantReadWriteLock instances for
|
||||||
|
// now.
|
||||||
|
private static HashMap<String, ReentrantReadWriteLock>
|
||||||
|
s_locks = new HashMap<String,ReentrantReadWriteLock>();
|
||||||
|
|
||||||
|
public GameLock( String path, boolean isForWrite )
|
||||||
|
{
|
||||||
|
m_path = path;
|
||||||
|
m_isForWrite = isForWrite;
|
||||||
|
synchronized( s_locks ) {
|
||||||
|
if ( s_locks.containsKey( m_path ) ) {
|
||||||
|
m_rwlock = s_locks.get( m_path );
|
||||||
|
} else {
|
||||||
|
m_rwlock = new ReentrantReadWriteLock();
|
||||||
|
s_locks.put( path, m_rwlock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameLock lock()
|
||||||
|
{
|
||||||
|
getLock().lock();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tryLock()
|
||||||
|
{
|
||||||
|
boolean gotIt = false;
|
||||||
|
synchronized( s_locks ) {
|
||||||
|
boolean hasWaiters = m_isForWrite && m_rwlock.isWriteLocked();
|
||||||
|
if ( !hasWaiters ) {
|
||||||
|
getLock().lock();
|
||||||
|
gotIt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gotIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlock()
|
||||||
|
{
|
||||||
|
synchronized( s_locks ) {
|
||||||
|
getLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath()
|
||||||
|
{
|
||||||
|
return m_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// used only for asserts
|
||||||
|
public boolean canWrite()
|
||||||
|
{
|
||||||
|
return m_isForWrite && m_rwlock.isWriteLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lock getLock()
|
||||||
|
{
|
||||||
|
Lock lock = m_isForWrite? m_rwlock.writeLock()
|
||||||
|
: m_rwlock.readLock();
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static GameSummary getSummary( Context context, String file )
|
public static GameSummary getSummary( Context context, String file )
|
||||||
|
{
|
||||||
|
DBUtils.GameLock lock = new DBUtils.GameLock( file, false ).lock();
|
||||||
|
GameSummary result = getSummary( context, lock );
|
||||||
|
lock.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameSummary getSummary( Context context, DBUtils.GameLock lock )
|
||||||
{
|
{
|
||||||
initDB( context );
|
initDB( context );
|
||||||
GameSummary summary = null;
|
GameSummary summary = null;
|
||||||
|
@ -80,7 +165,7 @@ public class DBUtils {
|
||||||
DBHelper.SCORES, DBHelper.HASMSGS,
|
DBHelper.SCORES, DBHelper.HASMSGS,
|
||||||
DBHelper.LASTPLAY_TIME
|
DBHelper.LASTPLAY_TIME
|
||||||
};
|
};
|
||||||
String selection = DBHelper.FILE_NAME + "=\"" + file + "\"";
|
String selection = DBHelper.FILE_NAME + "=\"" + lock.getPath() + "\"";
|
||||||
|
|
||||||
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
|
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
|
||||||
selection, null, null, null, null );
|
selection, null, null, null, null );
|
||||||
|
@ -168,15 +253,17 @@ public class DBUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( null == summary ) {
|
if ( null == summary ) {
|
||||||
summary = GameUtils.summarize( context, file );
|
summary = GameUtils.summarize( context, lock );
|
||||||
saveSummary( context, file, summary );
|
saveSummary( context, lock, summary );
|
||||||
}
|
}
|
||||||
return summary;
|
return summary;
|
||||||
} // getSummary
|
} // getSummary
|
||||||
|
|
||||||
public static void saveSummary( Context context, String path,
|
public static void saveSummary( Context context, DBUtils.GameLock lock,
|
||||||
GameSummary summary )
|
GameSummary summary )
|
||||||
{
|
{
|
||||||
|
Assert.assertTrue( lock.canWrite() );
|
||||||
|
String path = lock.getPath();
|
||||||
initDB( context );
|
initDB( context );
|
||||||
synchronized( s_dbHelper ) {
|
synchronized( s_dbHelper ) {
|
||||||
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
||||||
|
@ -416,9 +503,11 @@ public class DBUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveGame( Context context, String path, byte[] bytes,
|
public static void saveGame( Context context, GameLock lock, byte[] bytes,
|
||||||
boolean setCreate )
|
boolean setCreate )
|
||||||
{
|
{
|
||||||
|
Assert.assertTrue( lock.canWrite() );
|
||||||
|
String path = lock.getPath();
|
||||||
initDB( context );
|
initDB( context );
|
||||||
synchronized( s_dbHelper ) {
|
synchronized( s_dbHelper ) {
|
||||||
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
||||||
|
@ -445,8 +534,9 @@ public class DBUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] loadGame( Context context, String path )
|
public static byte[] loadGame( Context context, GameLock lock )
|
||||||
{
|
{
|
||||||
|
String path = lock.getPath();
|
||||||
Assert.assertNotNull( path );
|
Assert.assertNotNull( path );
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
initDB( context );
|
initDB( context );
|
||||||
|
@ -467,12 +557,12 @@ public class DBUtils {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteGame( Context context, String path )
|
public static void deleteGame( Context context, GameLock lock )
|
||||||
{
|
{
|
||||||
initDB( context );
|
initDB( context );
|
||||||
synchronized( s_dbHelper ) {
|
synchronized( s_dbHelper ) {
|
||||||
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
SQLiteDatabase db = s_dbHelper.getWritableDatabase();
|
||||||
String selection = DBHelper.FILE_NAME + "=\"" + path + "\"";
|
String selection = DBHelper.FILE_NAME + "=\"" + lock.getPath() + "\"";
|
||||||
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
|
db.delete( DBHelper.TABLE_NAME_SUM, selection, null );
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ public class GameConfig extends XWActivity
|
||||||
private String m_path;
|
private String m_path;
|
||||||
private CurGameInfo m_gi;
|
private CurGameInfo m_gi;
|
||||||
private CurGameInfo m_giOrig;
|
private CurGameInfo m_giOrig;
|
||||||
|
private DBUtils.GameLock m_gameLock;
|
||||||
private int m_whichPlayer;
|
private int m_whichPlayer;
|
||||||
// private Spinner m_roleSpinner;
|
// private Spinner m_roleSpinner;
|
||||||
// private Spinner m_connectSpinner;
|
// private Spinner m_connectSpinner;
|
||||||
|
@ -365,9 +366,37 @@ public class GameConfig extends XWActivity
|
||||||
|
|
||||||
setContentView(R.layout.game_config);
|
setContentView(R.layout.game_config);
|
||||||
|
|
||||||
|
|
||||||
|
m_connectSet = findViewById(R.id.connect_set);
|
||||||
|
m_addPlayerButton = (Button)findViewById(R.id.add_player);
|
||||||
|
m_addPlayerButton.setOnClickListener( this );
|
||||||
|
m_jugglePlayersButton = (Button)findViewById(R.id.juggle_players);
|
||||||
|
m_jugglePlayersButton.setOnClickListener( this );
|
||||||
|
m_playButton = (Button)findViewById( R.id.play_button );
|
||||||
|
m_playButton.setOnClickListener( this );
|
||||||
|
|
||||||
|
m_playerLayout = (LinearLayout)findViewById( R.id.player_list );
|
||||||
|
m_dictSpinner = (Spinner)findViewById( R.id.dict_spinner );
|
||||||
|
m_phoniesSpinner = (Spinner)findViewById( R.id.phonies_spinner );
|
||||||
|
m_smartnessSpinner = (Spinner)findViewById( R.id.smart_robot );
|
||||||
|
|
||||||
|
String fmt = getString( m_notNetworkedGame ?
|
||||||
|
R.string.title_game_configf
|
||||||
|
: R.string.title_gamenet_configf );
|
||||||
|
setTitle( String.format( fmt, GameUtils.gameName( this, m_path ) ) );
|
||||||
|
} // onCreate
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart()
|
||||||
|
{
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
int gamePtr = XwJNI.initJNI();
|
int gamePtr = XwJNI.initJNI();
|
||||||
m_giOrig = new CurGameInfo( this );
|
m_giOrig = new CurGameInfo( this );
|
||||||
GameUtils.loadMakeGame( this, gamePtr, m_giOrig, m_path );
|
// Lock in case we're going to config. We *could* re-get the
|
||||||
|
// lock once the user decides to make changes. PENDING.
|
||||||
|
m_gameLock = new DBUtils.GameLock( m_path, true ).lock();
|
||||||
|
GameUtils.loadMakeGame( this, gamePtr, m_giOrig, m_gameLock );
|
||||||
m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0
|
m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0
|
||||||
|| XwJNI.comms_isConnected( gamePtr );
|
|| XwJNI.comms_isConnected( gamePtr );
|
||||||
m_giOrig.setInProgress( m_gameStarted );
|
m_giOrig.setInProgress( m_gameStarted );
|
||||||
|
@ -423,25 +452,12 @@ public class GameConfig extends XWActivity
|
||||||
adjustConnectStuff();
|
adjustConnectStuff();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connectSet = findViewById(R.id.connect_set);
|
|
||||||
|
|
||||||
m_addPlayerButton = (Button)findViewById(R.id.add_player);
|
|
||||||
m_addPlayerButton.setOnClickListener( this );
|
|
||||||
m_jugglePlayersButton = (Button)findViewById(R.id.juggle_players);
|
|
||||||
m_jugglePlayersButton.setOnClickListener( this );
|
|
||||||
m_playButton = (Button)findViewById( R.id.play_button );
|
|
||||||
m_playButton.setOnClickListener( this );
|
|
||||||
|
|
||||||
m_playerLayout = (LinearLayout)findViewById( R.id.player_list );
|
|
||||||
loadPlayers();
|
loadPlayers();
|
||||||
|
|
||||||
m_dictSpinner = (Spinner)findViewById( R.id.dict_spinner );
|
|
||||||
configDictSpinner();
|
configDictSpinner();
|
||||||
|
|
||||||
m_phoniesSpinner = (Spinner)findViewById( R.id.phonies_spinner );
|
|
||||||
m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() );
|
m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() );
|
||||||
|
|
||||||
m_smartnessSpinner = (Spinner)findViewById( R.id.smart_robot );
|
|
||||||
setSmartnessSpinner();
|
setSmartnessSpinner();
|
||||||
|
|
||||||
Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed );
|
Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed );
|
||||||
|
@ -459,12 +475,17 @@ public class GameConfig extends XWActivity
|
||||||
};
|
};
|
||||||
check.setOnCheckedChangeListener( lstnr );
|
check.setOnCheckedChangeListener( lstnr );
|
||||||
Utils.setChecked( this, R.id.use_timer, m_gi.timerEnabled );
|
Utils.setChecked( this, R.id.use_timer, m_gi.timerEnabled );
|
||||||
|
} // onStart
|
||||||
|
|
||||||
String fmt = getString( m_notNetworkedGame ?
|
@Override
|
||||||
R.string.title_game_configf
|
protected void onStop()
|
||||||
: R.string.title_gamenet_configf );
|
{
|
||||||
setTitle( String.format( fmt, GameUtils.gameName( this, m_path ) ) );
|
if ( null != m_gameLock ) {
|
||||||
} // onCreate
|
m_gameLock.unlock();
|
||||||
|
m_gameLock = null;
|
||||||
|
}
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteCallback interface
|
// DeleteCallback interface
|
||||||
public void deleteCalled( int myPosition )
|
public void deleteCalled( int myPosition )
|
||||||
|
@ -889,7 +910,7 @@ public class GameConfig extends XWActivity
|
||||||
|
|
||||||
private void applyChanges( boolean forceNew )
|
private void applyChanges( boolean forceNew )
|
||||||
{
|
{
|
||||||
GameUtils.applyChanges( this, m_gi, m_car, m_path, forceNew );
|
GameUtils.applyChanges( this, m_gi, m_car, m_gameLock, forceNew );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchGame()
|
private void launchGame()
|
||||||
|
|
|
@ -37,7 +37,10 @@ public class GameConverter {
|
||||||
Utils.logf( "GameConverter::convert() converting %s",
|
Utils.logf( "GameConverter::convert() converting %s",
|
||||||
game );
|
game );
|
||||||
byte[] bytes = savedGame( context, game );
|
byte[] bytes = savedGame( context, game );
|
||||||
DBUtils.saveGame( context, game, bytes, true );
|
DBUtils.GameLock lock =
|
||||||
|
new DBUtils.GameLock( game, true ).lock();
|
||||||
|
DBUtils.saveGame( context, lock, bytes, true );
|
||||||
|
lock.unlock();
|
||||||
context.deleteFile( game );
|
context.deleteFile( game );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,15 +41,23 @@ public class GameUtils {
|
||||||
|
|
||||||
public static byte[] savedGame( Context context, String path )
|
public static byte[] savedGame( Context context, String path )
|
||||||
{
|
{
|
||||||
return DBUtils.loadGame( context, path );
|
DBUtils.GameLock lock = new DBUtils.GameLock( path, false ).lock();
|
||||||
|
byte[] result = savedGame( context, lock );
|
||||||
|
lock.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] savedGame( Context context, DBUtils.GameLock lock )
|
||||||
|
{
|
||||||
|
return DBUtils.loadGame( context, lock );
|
||||||
} // savedGame
|
} // savedGame
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open an existing game, and use its gi and comms addr as the
|
* Open an existing game, and use its gi and comms addr as the
|
||||||
* basis for a new one.
|
* basis for a new one.
|
||||||
*/
|
*/
|
||||||
public static void resetGame( Context context, String pathIn,
|
public static void resetGame( Context context, DBUtils.GameLock lockSrc,
|
||||||
String pathOut )
|
DBUtils.GameLock lockDest )
|
||||||
{
|
{
|
||||||
int gamePtr = XwJNI.initJNI();
|
int gamePtr = XwJNI.initJNI();
|
||||||
CurGameInfo gi = new CurGameInfo( context );
|
CurGameInfo gi = new CurGameInfo( context );
|
||||||
|
@ -57,7 +65,7 @@ public class GameUtils {
|
||||||
|
|
||||||
// loadMakeGame, if makinga new game, will add comms as long
|
// loadMakeGame, if makinga new game, will add comms as long
|
||||||
// as DeviceRole.SERVER_STANDALONE != gi.serverRole
|
// as DeviceRole.SERVER_STANDALONE != gi.serverRole
|
||||||
loadMakeGame( context, gamePtr, gi, pathIn );
|
loadMakeGame( context, gamePtr, gi, lockSrc );
|
||||||
byte[] dictBytes = GameUtils.openDict( context, gi.dictName );
|
byte[] dictBytes = GameUtils.openDict( context, gi.dictName );
|
||||||
|
|
||||||
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
||||||
|
@ -81,25 +89,28 @@ public class GameUtils {
|
||||||
XwJNI.comms_setAddr( gamePtr, addr );
|
XwJNI.comms_setAddr( gamePtr, addr );
|
||||||
}
|
}
|
||||||
|
|
||||||
saveGame( context, gamePtr, gi, pathOut, true );
|
saveGame( context, gamePtr, gi, lockDest, true );
|
||||||
summarizeAndClose( context, pathOut, gamePtr, gi );
|
summarizeAndClose( context, lockDest, gamePtr, gi );
|
||||||
} // resetGame
|
} // resetGame
|
||||||
|
|
||||||
public static void resetGame( Context context, String pathIn )
|
public static void resetGame( Context context, String pathIn )
|
||||||
{
|
{
|
||||||
tellRelayDied( context, pathIn, true );
|
DBUtils.GameLock lock = new DBUtils.GameLock( pathIn, true )
|
||||||
resetGame( context, pathIn, pathIn );
|
.lock();
|
||||||
|
tellRelayDied( context, lock, true );
|
||||||
|
resetGame( context, lock, lock );
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameSummary summarizeAndClose( Context context,
|
private static GameSummary summarizeAndClose( Context context,
|
||||||
String path,
|
DBUtils.GameLock lock,
|
||||||
int gamePtr, CurGameInfo gi )
|
int gamePtr, CurGameInfo gi )
|
||||||
{
|
{
|
||||||
return summarizeAndClose( context, path, gamePtr, gi, null );
|
return summarizeAndClose( context, lock, gamePtr, gi, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameSummary summarizeAndClose( Context context,
|
private static GameSummary summarizeAndClose( Context context,
|
||||||
String path,
|
DBUtils.GameLock lock,
|
||||||
int gamePtr, CurGameInfo gi,
|
int gamePtr, CurGameInfo gi,
|
||||||
FeedUtilsImpl feedImpl )
|
FeedUtilsImpl feedImpl )
|
||||||
{
|
{
|
||||||
|
@ -118,47 +129,55 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUtils.saveSummary( context, path, summary );
|
DBUtils.saveSummary( context, lock, summary );
|
||||||
|
|
||||||
XwJNI.game_dispose( gamePtr );
|
XwJNI.game_dispose( gamePtr );
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameSummary summarize( Context context, String path )
|
public static GameSummary summarize( Context context, DBUtils.GameLock lock )
|
||||||
{
|
{
|
||||||
int gamePtr = XwJNI.initJNI();
|
int gamePtr = XwJNI.initJNI();
|
||||||
CurGameInfo gi = new CurGameInfo( context );
|
CurGameInfo gi = new CurGameInfo( context );
|
||||||
loadMakeGame( context, gamePtr, gi, path );
|
loadMakeGame( context, gamePtr, gi, lock );
|
||||||
|
|
||||||
return summarizeAndClose( context, path, gamePtr, gi );
|
return summarizeAndClose( context, lock, gamePtr, gi );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String dupeGame( Context context, String pathIn )
|
public static String dupeGame( Context context, String pathIn )
|
||||||
{
|
{
|
||||||
|
DBUtils.GameLock lockSrc = new DBUtils.GameLock( pathIn, false ).lock();
|
||||||
String newName = newName( context );
|
String newName = newName( context );
|
||||||
resetGame( context, pathIn, newName );
|
DBUtils.GameLock lockDest =
|
||||||
|
new DBUtils.GameLock( newName, true ).lock();
|
||||||
|
resetGame( context, lockSrc, lockDest );
|
||||||
|
lockDest.unlock();
|
||||||
|
lockSrc.unlock();
|
||||||
return newName;
|
return newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteGame( Context context, String path,
|
public static void deleteGame( Context context, String path, boolean informNow )
|
||||||
boolean informNow )
|
|
||||||
{
|
{
|
||||||
// does this need to be synchronized?
|
// does this need to be synchronized?
|
||||||
tellRelayDied( context, path, informNow );
|
DBUtils.GameLock lock = new DBUtils.GameLock( path, true );
|
||||||
DBUtils.deleteGame( context, path );
|
if ( lock.tryLock() ) {
|
||||||
|
tellRelayDied( context, lock, informNow );
|
||||||
|
DBUtils.deleteGame( context, lock );
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadMakeGame( Context context, int gamePtr,
|
public static void loadMakeGame( Context context, int gamePtr,
|
||||||
CurGameInfo gi, String path )
|
CurGameInfo gi, DBUtils.GameLock lock )
|
||||||
{
|
{
|
||||||
loadMakeGame( context, gamePtr, gi, null, path );
|
loadMakeGame( context, gamePtr, gi, null, lock );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadMakeGame( Context context, int gamePtr,
|
public static void loadMakeGame( Context context, int gamePtr,
|
||||||
CurGameInfo gi, UtilCtxt util,
|
CurGameInfo gi, UtilCtxt util,
|
||||||
String path )
|
DBUtils.GameLock lock )
|
||||||
{
|
{
|
||||||
byte[] stream = savedGame( context, path );
|
byte[] stream = savedGame( context, lock );
|
||||||
XwJNI.gi_from_stream( gi, stream );
|
XwJNI.gi_from_stream( gi, stream );
|
||||||
byte[] dictBytes = GameUtils.openDict( context, gi.dictName );
|
byte[] dictBytes = GameUtils.openDict( context, gi.dictName );
|
||||||
|
|
||||||
|
@ -175,30 +194,36 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveGame( Context context, int gamePtr,
|
public static void saveGame( Context context, int gamePtr,
|
||||||
CurGameInfo gi, String path,
|
CurGameInfo gi, DBUtils.GameLock lock,
|
||||||
boolean setCreate )
|
boolean setCreate )
|
||||||
{
|
{
|
||||||
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
||||||
saveGame( context, stream, path, setCreate );
|
saveGame( context, stream, lock, setCreate );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveGame( Context context, int gamePtr,
|
public static void saveGame( Context context, int gamePtr,
|
||||||
CurGameInfo gi )
|
CurGameInfo gi )
|
||||||
{
|
{
|
||||||
saveGame( context, gamePtr, gi, newName( context ), false );
|
String path = newName( context );
|
||||||
|
DBUtils.GameLock lock =
|
||||||
|
new DBUtils.GameLock( path, true ).lock();
|
||||||
|
saveGame( context, gamePtr, gi, lock, false );
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveGame( Context context, byte[] bytes,
|
public static void saveGame( Context context, byte[] bytes,
|
||||||
String path, boolean setCreate )
|
DBUtils.GameLock lock, boolean setCreate )
|
||||||
{
|
{
|
||||||
DBUtils.saveGame( context, path, bytes, setCreate );
|
DBUtils.saveGame( context, lock, bytes, setCreate );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String saveGame( Context context, byte[] bytes )
|
public static DBUtils.GameLock saveGame( Context context, byte[] bytes )
|
||||||
{
|
{
|
||||||
String name = newName( context );
|
String name = newName( context );
|
||||||
saveGame( context, bytes, name, false );
|
DBUtils.GameLock lock =
|
||||||
return name;
|
new DBUtils.GameLock( name, true ).lock();
|
||||||
|
saveGame( context, bytes, lock, false );
|
||||||
|
return lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean gameDictHere( Context context, String path )
|
public static boolean gameDictHere( Context context, String path )
|
||||||
|
@ -443,30 +468,34 @@ public class GameUtils {
|
||||||
int gamePtr = XwJNI.initJNI();
|
int gamePtr = XwJNI.initJNI();
|
||||||
CurGameInfo gi = new CurGameInfo( context );
|
CurGameInfo gi = new CurGameInfo( context );
|
||||||
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, path );
|
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, path );
|
||||||
loadMakeGame( context, gamePtr, gi, feedImpl, path );
|
DBUtils.GameLock lock = new DBUtils.GameLock( path, true );
|
||||||
|
if ( lock.tryLock() ) {
|
||||||
|
loadMakeGame( context, gamePtr, gi, feedImpl, lock );
|
||||||
|
|
||||||
for ( byte[] msg : msgs ) {
|
for ( byte[] msg : msgs ) {
|
||||||
draw = XwJNI.game_receiveMessage( gamePtr, msg ) || draw;
|
draw = XwJNI.game_receiveMessage( gamePtr, msg ) || draw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, gi );
|
||||||
saveGame( context, gamePtr, gi, path, false );
|
saveGame( context, gamePtr, gi, lock, false );
|
||||||
summarizeAndClose( context, path, gamePtr, gi, feedImpl );
|
summarizeAndClose( context, lock, gamePtr, gi, feedImpl );
|
||||||
|
|
||||||
int flags = GameSummary.MSG_FLAGS_NONE;
|
int flags = GameSummary.MSG_FLAGS_NONE;
|
||||||
if ( feedImpl.m_gotChat ) {
|
if ( feedImpl.m_gotChat ) {
|
||||||
flags |= GameSummary.MSG_FLAGS_CHAT;
|
flags |= GameSummary.MSG_FLAGS_CHAT;
|
||||||
}
|
}
|
||||||
if ( feedImpl.m_gotMsg ) {
|
if ( feedImpl.m_gotMsg ) {
|
||||||
flags |= GameSummary.MSG_FLAGS_TURN;
|
flags |= GameSummary.MSG_FLAGS_TURN;
|
||||||
}
|
}
|
||||||
if ( feedImpl.m_gameOver ) {
|
if ( feedImpl.m_gameOver ) {
|
||||||
flags |= GameSummary.MSG_FLAGS_GAMEOVER;
|
flags |= GameSummary.MSG_FLAGS_GAMEOVER;
|
||||||
}
|
}
|
||||||
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
|
||||||
draw = true;
|
draw = true;
|
||||||
DBUtils.setMsgFlags( path, flags );
|
DBUtils.setMsgFlags( path, flags );
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Utils.logf( "feedMessages=>%s", draw?"true":"false" );
|
Utils.logf( "feedMessages=>%s", draw?"true":"false" );
|
||||||
|
@ -479,7 +508,8 @@ public class GameUtils {
|
||||||
public static void replaceDict( Context context, String path,
|
public static void replaceDict( Context context, String path,
|
||||||
String dict )
|
String dict )
|
||||||
{
|
{
|
||||||
byte[] stream = savedGame( context, path );
|
DBUtils.GameLock lock = new DBUtils.GameLock( path, true ).lock();
|
||||||
|
byte[] stream = savedGame( context, lock );
|
||||||
CurGameInfo gi = new CurGameInfo( context );
|
CurGameInfo gi = new CurGameInfo( context );
|
||||||
byte[] dictBytes = GameUtils.openDict( context, dict );
|
byte[] dictBytes = GameUtils.openDict( context, dict );
|
||||||
|
|
||||||
|
@ -490,13 +520,15 @@ public class GameUtils {
|
||||||
CommonPrefs.get( context ) );
|
CommonPrefs.get( context ) );
|
||||||
gi.dictName = dict;
|
gi.dictName = dict;
|
||||||
|
|
||||||
saveGame( context, gamePtr, gi, path, false );
|
saveGame( context, gamePtr, gi, lock, false );
|
||||||
|
|
||||||
summarizeAndClose( context, path, gamePtr, gi );
|
summarizeAndClose( context, lock, gamePtr, gi );
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyChanges( Context context, CurGameInfo gi,
|
public static void applyChanges( Context context, CurGameInfo gi,
|
||||||
CommsAddrRec car, String path,
|
CommsAddrRec car, DBUtils.GameLock lock,
|
||||||
boolean forceNew )
|
boolean forceNew )
|
||||||
{
|
{
|
||||||
// This should be a separate function, commitChanges() or
|
// This should be a separate function, commitChanges() or
|
||||||
|
@ -509,9 +541,9 @@ public class GameUtils {
|
||||||
CommonPrefs cp = CommonPrefs.get( context );
|
CommonPrefs cp = CommonPrefs.get( context );
|
||||||
|
|
||||||
if ( forceNew ) {
|
if ( forceNew ) {
|
||||||
tellRelayDied( context, path, true );
|
tellRelayDied( context, lock, true );
|
||||||
} else {
|
} else {
|
||||||
byte[] stream = GameUtils.savedGame( context, path );
|
byte[] stream = GameUtils.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, stream,
|
||||||
JNIUtilsImpl.get(),
|
JNIUtilsImpl.get(),
|
||||||
|
@ -529,11 +561,11 @@ public class GameUtils {
|
||||||
XwJNI.comms_setAddr( gamePtr, car );
|
XwJNI.comms_setAddr( gamePtr, car );
|
||||||
}
|
}
|
||||||
|
|
||||||
saveGame( context, gamePtr, gi, path, false );
|
saveGame( context, gamePtr, gi, lock, false );
|
||||||
|
|
||||||
GameSummary summary = new GameSummary( gi );
|
GameSummary summary = new GameSummary( gi );
|
||||||
XwJNI.game_summarize( gamePtr, summary );
|
XwJNI.game_summarize( gamePtr, summary );
|
||||||
DBUtils.saveSummary( context, path, summary );
|
DBUtils.saveSummary( context, lock, summary );
|
||||||
|
|
||||||
XwJNI.game_dispose( gamePtr );
|
XwJNI.game_dispose( gamePtr );
|
||||||
} // applyChanges
|
} // applyChanges
|
||||||
|
@ -573,10 +605,10 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tellRelayDied( Context context, String path,
|
private static void tellRelayDied( Context context, DBUtils.GameLock lock,
|
||||||
boolean informNow )
|
boolean informNow )
|
||||||
{
|
{
|
||||||
GameSummary summary = DBUtils.getSummary( context, path );
|
GameSummary summary = DBUtils.getSummary( context, lock );
|
||||||
if ( null != summary.relayID ) {
|
if ( null != summary.relayID ) {
|
||||||
DBUtils.addDeceased( context, summary.relayID, summary.seed );
|
DBUtils.addDeceased( context, summary.relayID, summary.seed );
|
||||||
if ( informNow ) {
|
if ( informNow ) {
|
||||||
|
|
|
@ -431,8 +431,9 @@ public class GamesList extends XWListActivity
|
||||||
showOKOnlyDialog( R.string.no_copy_network );
|
showOKOnlyDialog( R.string.no_copy_network );
|
||||||
} else {
|
} else {
|
||||||
byte[] stream = GameUtils.savedGame( this, path );
|
byte[] stream = GameUtils.savedGame( this, path );
|
||||||
newName = GameUtils.saveGame( this, stream );
|
DBUtils.GameLock lock = GameUtils.saveGame( this, stream );
|
||||||
DBUtils.saveSummary( this, newName, summary );
|
DBUtils.saveSummary( this, lock, summary );
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -486,7 +487,9 @@ public class GamesList extends XWListActivity
|
||||||
String path = null;
|
String path = null;
|
||||||
byte[] bytes = XwJNI.gi_to_stream( gi );
|
byte[] bytes = XwJNI.gi_to_stream( gi );
|
||||||
if ( null != bytes ) {
|
if ( null != bytes ) {
|
||||||
path = GameUtils.saveGame( this, bytes );
|
DBUtils.GameLock lock = GameUtils.saveGame( this, bytes );
|
||||||
|
path = lock.getPath();
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class RelayGameActivity extends XWActivity
|
||||||
|
|
||||||
private String m_path;
|
private String m_path;
|
||||||
private CurGameInfo m_gi;
|
private CurGameInfo m_gi;
|
||||||
|
private DBUtils.GameLock m_gameLock;
|
||||||
private CommsAddrRec m_car;
|
private CommsAddrRec m_car;
|
||||||
private Button m_playButton;
|
private Button m_playButton;
|
||||||
private Button m_configButton;
|
private Button m_configButton;
|
||||||
|
@ -59,9 +60,22 @@ public class RelayGameActivity extends XWActivity
|
||||||
m_path = m_path.substring( 1 );
|
m_path = m_path.substring( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_playButton = (Button)findViewById( R.id.play_button );
|
||||||
|
m_playButton.setOnClickListener( this );
|
||||||
|
|
||||||
|
m_configButton = (Button)findViewById( R.id.config_button );
|
||||||
|
m_configButton.setOnClickListener( this );
|
||||||
|
} // onCreate
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart()
|
||||||
|
{
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
int gamePtr = XwJNI.initJNI();
|
int gamePtr = XwJNI.initJNI();
|
||||||
m_gi = new CurGameInfo( this );
|
m_gi = new CurGameInfo( this );
|
||||||
GameUtils.loadMakeGame( this, gamePtr, m_gi, m_path );
|
m_gameLock = new DBUtils.GameLock( m_path, true ).lock();
|
||||||
|
GameUtils.loadMakeGame( this, gamePtr, m_gi, m_gameLock );
|
||||||
m_car = new CommsAddrRec( this );
|
m_car = new CommsAddrRec( this );
|
||||||
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
||||||
XwJNI.comms_getAddr( gamePtr, m_car );
|
XwJNI.comms_getAddr( gamePtr, m_car );
|
||||||
|
@ -77,13 +91,17 @@ public class RelayGameActivity extends XWActivity
|
||||||
String fmt = getString( R.string.relay_game_explainf );
|
String fmt = getString( R.string.relay_game_explainf );
|
||||||
TextView text = (TextView)findViewById( R.id.explain );
|
TextView text = (TextView)findViewById( R.id.explain );
|
||||||
text.setText( String.format( fmt, lang ) );
|
text.setText( String.format( fmt, lang ) );
|
||||||
|
}
|
||||||
|
|
||||||
m_playButton = (Button)findViewById( R.id.play_button );
|
@Override
|
||||||
m_playButton.setOnClickListener( this );
|
protected void onPause()
|
||||||
|
{
|
||||||
m_configButton = (Button)findViewById( R.id.config_button );
|
if ( null != m_gameLock ) {
|
||||||
m_configButton.setOnClickListener( this );
|
m_gameLock.unlock();
|
||||||
} // onCreate
|
m_gameLock = null;
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick( View view )
|
public void onClick( View view )
|
||||||
|
@ -115,7 +133,9 @@ public class RelayGameActivity extends XWActivity
|
||||||
m_gi.setFirstLocalName( name );
|
m_gi.setFirstLocalName( name );
|
||||||
}
|
}
|
||||||
m_car.ip_relay_invite = room;
|
m_car.ip_relay_invite = room;
|
||||||
GameUtils.applyChanges( this, m_gi, m_car, m_path, false );
|
GameUtils.applyChanges( this, m_gi, m_car, m_gameLock, false );
|
||||||
|
m_gameLock.unlock();
|
||||||
|
m_gameLock = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // class RelayGameActivity
|
} // class RelayGameActivity
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class JNIThread extends Thread {
|
||||||
|
|
||||||
private boolean m_stopped = false;
|
private boolean m_stopped = false;
|
||||||
private int m_jniGamePtr;
|
private int m_jniGamePtr;
|
||||||
private String m_path;
|
private DBUtils.GameLock m_lock;
|
||||||
private Context m_context;
|
private Context m_context;
|
||||||
private CurGameInfo m_gi;
|
private CurGameInfo m_gi;
|
||||||
private Handler m_handler;
|
private Handler m_handler;
|
||||||
|
@ -111,12 +111,12 @@ public class JNIThread extends Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
public JNIThread( int gamePtr, CurGameInfo gi, SyncedDraw drawer,
|
public JNIThread( int gamePtr, CurGameInfo gi, SyncedDraw drawer,
|
||||||
String path, Context context, Handler handler )
|
DBUtils.GameLock lock, Context context, Handler handler )
|
||||||
{
|
{
|
||||||
m_jniGamePtr = gamePtr;
|
m_jniGamePtr = gamePtr;
|
||||||
m_gi = gi;
|
m_gi = gi;
|
||||||
m_drawer = drawer;
|
m_drawer = drawer;
|
||||||
m_path = path;
|
m_lock = lock;
|
||||||
m_context = context;
|
m_context = context;
|
||||||
m_handler = handler;
|
m_handler = handler;
|
||||||
|
|
||||||
|
@ -260,8 +260,8 @@ public class JNIThread extends Thread {
|
||||||
GameSummary summary = new GameSummary( m_gi );
|
GameSummary summary = new GameSummary( m_gi );
|
||||||
XwJNI.game_summarize( m_jniGamePtr, summary );
|
XwJNI.game_summarize( m_jniGamePtr, summary );
|
||||||
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, null );
|
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, null );
|
||||||
GameUtils.saveGame( m_context, state, m_path, false );
|
GameUtils.saveGame( m_context, state, m_lock, false );
|
||||||
DBUtils.saveSummary( m_context, m_path, summary );
|
DBUtils.saveSummary( m_context, m_lock, summary );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_DRAW:
|
case CMD_DRAW:
|
||||||
|
|
Loading…
Add table
Reference in a new issue