Merge "croupier: Add Player Split View"
diff --git a/go/src/hearts/img/reposition/reposition.go b/go/src/hearts/img/reposition/reposition.go
index 926cf13..cd72e9c 100644
--- a/go/src/hearts/img/reposition/reposition.go
+++ b/go/src/hearts/img/reposition/reposition.go
@@ -278,6 +278,7 @@
}
func AnimateInSplit(u *uistate.UIState) {
+ ResetAnims(u)
topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
tableImgs := make([]*staticimg.StaticImg, 0)
bannerImgs := make([]*staticimg.StaticImg, 0)
@@ -322,6 +323,7 @@
}
func AnimateOutSplit(ch chan bool, u *uistate.UIState) {
+ ResetAnims(u)
topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
tableImgs := make([]*staticimg.StaticImg, 0)
bannerImgs := make([]*staticimg.StaticImg, 0)
@@ -570,3 +572,10 @@
return
}
}
+
+func ResetAnims(u *uistate.UIState) {
+ for _, ch := range u.AnimChans {
+ ch <- true
+ }
+ u.AnimChans = make([]chan bool, 0)
+}
diff --git a/go/src/hearts/img/uistate/uistate.go b/go/src/hearts/img/uistate/uistate.go
index 75fff63..392a00f 100644
--- a/go/src/hearts/img/uistate/uistate.go
+++ b/go/src/hearts/img/uistate/uistate.go
@@ -84,6 +84,7 @@
Overlap *coords.Vec
Padding float32
CurView View // the screen currently being shown to the user
+ ViewOnTouch View // the view at the beginning of a touch action
CurTable *table.Table // the table of the current game
Done bool // true if the app has been quit
Texs map[string]sprite.SubTex // map of all loaded images
diff --git a/go/src/hearts/img/view/view.go b/go/src/hearts/img/view/view.go
index 87a4a2b..a325a4c 100644
--- a/go/src/hearts/img/view/view.go
+++ b/go/src/hearts/img/view/view.go
@@ -29,7 +29,7 @@
func LoadArrangeView(u *uistate.UIState) {
u.CurView = uistate.Arrange
<-time.After(1 * time.Second)
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addHeader(u)
@@ -63,7 +63,7 @@
// Waiting view: Displays the word "Waiting". To be displayed when players are waiting for a new round to be dealt
// TODO(emshack): Integrate this with Arrange view and Score view so that a separate screen is not necessary
func LoadWaitingView(u *uistate.UIState) {
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
center := u.WindowSize.DividedBy(2)
@@ -78,7 +78,7 @@
// Discovery view: Displays a menu of possible games to join
func LoadDiscoveryView(discChan chan []string, u *uistate.UIState) {
u.CurView = uistate.Discovery
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
newGameImg := u.Texs["NewGame.png"]
@@ -101,7 +101,7 @@
// Table View: Displays the table. Intended for public devices
func LoadTableView(u *uistate.UIState) {
u.CurView = uistate.Table
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
scaler := float32(6)
@@ -124,6 +124,7 @@
dropCard := u.CurTable.GetTrick()[0]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.Cards = append(u.Cards, dropCard)
}
@@ -141,6 +142,7 @@
dropCard = u.CurTable.GetTrick()[1]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.Cards = append(u.Cards, dropCard)
}
@@ -158,6 +160,7 @@
dropCard = u.CurTable.GetTrick()[2]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.Cards = append(u.Cards, dropCard)
}
@@ -175,6 +178,7 @@
dropCard = u.CurTable.GetTrick()[3]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.Cards = append(u.Cards, dropCard)
}
@@ -313,6 +317,7 @@
texture.PopulateCardImage(c, u)
c.SetBackDisplay(u.Eng)
pos := reposition.DetermineTablePassPosition(c, i, p.GetPlayerIndex(), u)
+ c.SetInitial(pos)
c.Move(pos, u.TableCardDim, u.Eng)
u.TableCards = append(u.TableCards, c)
}
@@ -340,7 +345,7 @@
// Score View: Shows current player standings at the end of every round, including the end of the game
func LoadScoreView(roundScores, winners []int, u *uistate.UIState) {
u.CurView = uistate.Score
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addHeader(u)
@@ -352,7 +357,7 @@
// Pass View: Shows player's hand and allows them to pass cards
func LoadPassView(u *uistate.UIState) {
u.CurView = uistate.Pass
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addHeader(u)
@@ -367,7 +372,7 @@
// Take View: Shows player's hand and allows them to take the cards that have been passed to them
func LoadTakeView(u *uistate.UIState) {
u.CurView = uistate.Take
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addHeader(u)
@@ -384,7 +389,7 @@
// Play View: Shows player's hand and allows them to play cards
func LoadPlayView(u *uistate.UIState) {
u.CurView = uistate.Play
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addPlaySlot(u)
@@ -408,7 +413,7 @@
func LoadSplitView(reloading bool, u *uistate.UIState) {
u.CurView = uistate.Split
- resetAnims(u)
+ reposition.ResetAnims(u)
resetImgs(u)
resetScene(u)
addPlayHeader(getTurnText(u), !reloading, u)
@@ -503,6 +508,7 @@
dropCard := u.CurTable.GetTrick()[u.CurPlayerIndex]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.TableCards = append(u.TableCards, dropCard)
}
@@ -523,6 +529,7 @@
dropCard = u.CurTable.GetTrick()[(u.CurPlayerIndex+1)%len(u.CurTable.GetPlayers())]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.TableCards = append(u.TableCards, dropCard)
}
@@ -543,6 +550,7 @@
dropCard = u.CurTable.GetTrick()[(u.CurPlayerIndex+2)%len(u.CurTable.GetPlayers())]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.TableCards = append(u.TableCards, dropCard)
}
@@ -563,6 +571,7 @@
dropCard = u.CurTable.GetTrick()[(u.CurPlayerIndex+3)%len(u.CurTable.GetPlayers())]
if dropCard != nil {
texture.PopulateCardImage(dropCard, u)
+ dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
u.TableCards = append(u.TableCards, dropCard)
}
@@ -1081,13 +1090,6 @@
})
}
-func resetAnims(u *uistate.UIState) {
- for _, ch := range u.AnimChans {
- ch <- true
- }
- u.AnimChans = make([]chan bool, 0)
-}
-
func addDebugBar(u *uistate.UIState) {
buttonDim := u.CardDim
debugTableImage := u.Texs["BakuSquare.png"]
diff --git a/go/src/hearts/sync/client.go b/go/src/hearts/sync/client.go
index f7bc1f4..c126c7c 100644
--- a/go/src/hearts/sync/client.go
+++ b/go/src/hearts/sync/client.go
@@ -84,6 +84,15 @@
return db.Watch(u.Ctx, tableName, prefix, resumeMarker)
}
+// Returns a scanstream of the data in the table
+func ScanData(tableName, prefix string, u *uistate.UIState) nosql.ScanStream {
+ app := u.Service.App(AppName)
+ db := app.NoSQLDatabase(DbName, nil)
+ table := db.Table(tableName)
+ rowRange := nosql.Range(prefix, "")
+ return table.Scan(u.Ctx, rowRange)
+}
+
// Joins gamelog syncgroup
func JoinLogSyncgroup(ch chan bool, logName string, u *uistate.UIState) {
fmt.Println("Joining gamelog syncgroup")
diff --git a/go/src/hearts/sync/server.go b/go/src/hearts/sync/server.go
index 9acb8c7..155830d 100644
--- a/go/src/hearts/sync/server.go
+++ b/go/src/hearts/sync/server.go
@@ -119,6 +119,7 @@
u.IsOwner = true
// Generate random gameID information to advertise this game
gameID := rand.Intn(1000000)
+ u.GameID = gameID
gameMap := make(map[string]interface{})
gameMap["type"] = "Hearts"
gameMap["playerNumber"] = 0
@@ -139,7 +140,7 @@
"Resolve": allAccess,
"Debug": allAccess,
}
- logPref := wire.TableRow{LogName, ""}
+ logPref := wire.TableRow{LogName, fmt.Sprintf("%d", u.GameID)}
logPrefs := []wire.TableRow{logPref}
tables := []string{MountPoint + "/croupier"}
logSpec := wire.SyncgroupSpec{
@@ -156,10 +157,16 @@
err = logSG.Create(u.Ctx, logSpec, myInfoCreator)
if err != nil {
fmt.Println("SYNCGROUP CREATE ERROR: ", err)
- ch <- ""
+ fmt.Println("JOINING INSTEAD...")
+ _, err2 := logSG.Join(u.Ctx, myInfoCreator)
+ if err2 != nil {
+ fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
+ ch <- ""
+ } else {
+ ch <- logSGName
+ }
} else {
fmt.Println("Syncgroup created")
- u.GameID = gameID
go UpdateGame(u)
ch <- logSGName
}
@@ -194,7 +201,14 @@
err := settingsSG.Create(u.Ctx, settingsSpec, myInfoCreator)
if err != nil {
fmt.Println("SYNCGROUP CREATE ERROR: ", err)
- ch <- ""
+ fmt.Println("JOINING INSTEAD...")
+ _, err2 := settingsSG.Join(u.Ctx, myInfoCreator)
+ if err2 != nil {
+ fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
+ ch <- ""
+ } else {
+ ch <- settingsSGName
+ }
} else {
fmt.Println("Syncgroup created")
ch <- settingsSGName
diff --git a/go/src/hearts/sync/util.go b/go/src/hearts/sync/util.go
index 5e1ebcc..cf72edd 100644
--- a/go/src/hearts/sync/util.go
+++ b/go/src/hearts/sync/util.go
@@ -8,16 +8,16 @@
const (
// switch back to my mountpoint with the following code:
- //MountPoint = "users/emshack@google.com"
- MountPoint = "/192.168.86.254:8101"
- UserID = 2222
+ MountPoint = "users/emshack@google.com"
+ //MountPoint = "/192.168.86.254:8101"
+ UserID = 4444
UserColor = 16777215
- UserAvatar = "player1.jpeg"
- UserName = "Bob"
- SBName = "syncbase1"
+ UserAvatar = "player3.jpeg"
+ UserName = "Dan"
+ SBName = "syncbase3"
AppName = "app"
DbName = "db"
LogName = "games"
SettingsName = "table_settings"
- CroupierInterface = "CroupierSettingsAndGame"
+ CroupierInterface = "CroupierSettingsAndGameEmily"
)
diff --git a/go/src/hearts/sync/watch.go b/go/src/hearts/sync/watch.go
index cb4effe..9cc2e87 100644
--- a/go/src/hearts/sync/watch.go
+++ b/go/src/hearts/sync/watch.go
@@ -24,6 +24,19 @@
)
func UpdateSettings(u *uistate.UIState) {
+ scanner := ScanData(SettingsName, "users", u)
+ for {
+ if updateExists := scanner.Advance(); updateExists {
+ key := scanner.Key()
+ var value []byte
+ if err := scanner.Value(&value); err != nil {
+ fmt.Println("Value error:", err)
+ }
+ handleSettingsUpdate(key, value, u)
+ } else {
+ break
+ }
+ }
stream, err := WatchData(SettingsName, "users", u)
if err != nil {
fmt.Println("WatchData error:", err)
@@ -32,42 +45,46 @@
if updateExists := stream.Advance(); updateExists {
c := stream.Change()
if c.ChangeType == nosql.PutChange {
+ key := c.Row
var value []byte
if err := c.Value(&value); err != nil {
fmt.Println("Value error:", err)
}
- var valueMap map[string]interface{}
- err := json.Unmarshal(value, &valueMap)
- if err != nil {
- fmt.Println("Unmarshal error:", err)
- }
- key := c.Row
- userID, _ := strconv.Atoi(strings.Split(key, "/")[1])
- u.UserData[userID] = valueMap
- for _, v := range u.PlayerData {
- if v == userID {
- switch u.CurView {
- case uistate.Arrange:
- view.LoadArrangeView(u)
- case uistate.Table:
- view.LoadTableView(u)
- case uistate.Pass:
- view.LoadPassView(u)
- case uistate.Take:
- view.LoadTakeView(u)
- case uistate.Play:
- view.LoadPlayView(u)
- case uistate.Split:
- view.LoadSplitView(true, u)
- }
- }
- }
+ handleSettingsUpdate(key, value, u)
} else {
fmt.Println("Unexpected ChangeType: ", c.ChangeType)
}
}
}
-}
+}
+
+func handleSettingsUpdate(key string, value []byte, u *uistate.UIState) {
+ var valueMap map[string]interface{}
+ err := json.Unmarshal(value, &valueMap)
+ if err != nil {
+ fmt.Println("Unmarshal error:", err)
+ }
+ userID, _ := strconv.Atoi(strings.Split(key, "/")[1])
+ u.UserData[userID] = valueMap
+ for _, v := range u.PlayerData {
+ if v == userID {
+ switch u.CurView {
+ case uistate.Arrange:
+ view.LoadArrangeView(u)
+ case uistate.Table:
+ view.LoadTableView(u)
+ case uistate.Pass:
+ view.LoadPassView(u)
+ case uistate.Take:
+ view.LoadTakeView(u)
+ case uistate.Play:
+ view.LoadPlayView(u)
+ case uistate.Split:
+ view.LoadSplitView(true, u)
+ }
+ }
+ }
+}
func UpdateGame(u *uistate.UIState) {
stream, err := WatchData(LogName, fmt.Sprintf("%d", u.GameID), u)
@@ -87,8 +104,6 @@
valueStr := string(value)
fmt.Println(key, valueStr)
keyType := strings.Split(key, "/")[1]
- gameID := strings.Split(key, "/")[0]
- fmt.Println("GAME ID:", gameID)
switch keyType {
case "log":
updateType := strings.Split(valueStr, "|")[0]
diff --git a/go/src/hearts/touchhandler/touchhandler.go b/go/src/hearts/touchhandler/touchhandler.go
index 0b4dc27..9283e23 100644
--- a/go/src/hearts/touchhandler/touchhandler.go
+++ b/go/src/hearts/touchhandler/touchhandler.go
@@ -22,6 +22,11 @@
)
func OnTouch(t touch.Event, u *uistate.UIState) {
+ if t.Type == touch.TypeBegin {
+ u.ViewOnTouch = u.CurView
+ } else if u.CurView != u.ViewOnTouch {
+ return
+ }
switch u.CurView {
case uistate.Discovery:
switch t.Type {
@@ -459,16 +464,10 @@
removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
- reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
- } else {
- reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
}
} else {
- // check to see if card was removed from a drop target
- removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
- reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
}
}
diff --git a/lib/logic/croupier.dart b/lib/logic/croupier.dart
index 70747ac..e3de247 100644
--- a/lib/logic/croupier.dart
+++ b/lib/logic/croupier.dart
@@ -6,6 +6,7 @@
import '../settings/client.dart' show AppSettings;
import '../src/syncbase/settings_manager.dart' show SettingsManager;
+import '../src/syncbase/util.dart' as sync_util;
import 'create_game.dart' as cg;
import 'croupier_settings.dart' show CroupierSettings;
import 'game/game.dart'
@@ -90,7 +91,12 @@
return null;
}
- void _updatePlayerFoundCb(String playerID, String playerNum) {
+ void _updatePlayerFoundCb(String playerKey, String playerNum) {
+ String gameIDStr = sync_util.gameIDFromGameKey(playerKey);
+ if (game == null || game.gameID != int.parse(gameIDStr)) {
+ return; // ignore
+ }
+ String playerID = sync_util.playerIDFromPlayerKey(playerKey);
int id = int.parse(playerID);
if (playerNum == null) {
if (!players_found.containsKey(id)) {
@@ -112,11 +118,15 @@
}
void _updateGameStatusCb(String statusKey, String newStatus) {
+ String gameIDStr = sync_util.gameIDFromGameKey(statusKey);
+ if (game == null || game.gameID != int.parse(gameIDStr)) {
+ return; // ignore
+ }
switch (newStatus) {
case "RUNNING":
if (state == CroupierState.ArrangePlayers) {
game.startGameSignal();
- state = CroupierState.PlayGame;
+ setState(CroupierState.PlayGame, null);
}
break;
default:
@@ -194,12 +204,12 @@
});
}
+ // The signal to start or quit is not anything special.
// data should be empty.
- // All rearrangements affect the Game's player number without changing app state.
+ assert(data == null);
break;
case CroupierState.PlayGame:
// data should be empty.
- // The signal to start really isn't anything special.
break;
default:
assert(false);
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index 72fb6c3..a635060 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -278,6 +278,10 @@
if (!this.isPlayer) {
this.viewType = HeartsType.Board;
}
+ // Only the creator should deal the cards once everyone is ready.
+ if (this.isCreator) {
+ this.dealCards();
+ }
}
// Note that this will be called by the UI.
@@ -327,10 +331,6 @@
this.resetGame();
print('we are all ready. ${isCreator}');
- // Only the creator should deal the cards once everyone is ready.
- if (this.isCreator) {
- this.dealCards();
- }
}
return;
case HeartsPhase.Deal:
diff --git a/lib/src/mocks/util.dart b/lib/src/mocks/util.dart
index e466b8d..0413ea1 100644
--- a/lib/src/mocks/util.dart
+++ b/lib/src/mocks/util.dart
@@ -3,3 +3,11 @@
// license that can be found in the LICENSE file.
typedef void keyValueCallback(String key, String value);
+
+String gameIDFromGameKey(String gameKey) {
+ return null;
+}
+
+String playerIDFromPlayerKey(String playerKey) {
+ return null;
+}
diff --git a/lib/src/syncbase/log_writer.dart b/lib/src/syncbase/log_writer.dart
index 3e58094..bfb278e 100644
--- a/lib/src/syncbase/log_writer.dart
+++ b/lib/src/syncbase/log_writer.dart
@@ -176,17 +176,6 @@
await _writeData(propKey, proposalData);
proposalsKnown[propKey] = proposalData;
- // TODO(alexfandrianto): Remove when we have 4 players going at once.
- // For quick development purposes, we may wish to keep this block.
- // FAKE: Do some bonus work. Where "everyone else" accepts the proposal.
- // Normally, one would rely on watch and the syncgroup peers to do this.
- /*for (int i = 0; i < users.length; i++) {
- if (users[i] != associatedUser) {
- // DO NOT AWAIT HERE. It must be done "asynchronously".
- _writeData(_proposalKey(users[i]), proposalData);
- }
- }*/
-
return;
}
await _writeData(key, value);
@@ -194,26 +183,16 @@
// Helper that writes data to the "store" and calls the update callback.
Future _writeData(String key, String value) async {
- var row = tb.row("${this.logPrefix}/${key}");
+ var row = tb.row(_rowKey(key));
await row.put(UTF8.encode(value));
}
- /*
- // _readData could be helpful eventually, but it's not needed yet.
- Future<String> _readData(String key) async {
- var row = tb.row("${this.logPrefix}/${key}");
- if (!(await row.exists())) {
- print("${key} did not exist");
- return null;
- }
- var getBytes = await row.get();
-
- return UTF8.decode(getBytes);
+ String _rowKey(String key) {
+ return "${this.logPrefix}/${key}";
}
- */
Future _deleteData(String key) async {
- var row = tb.row(key);
+ var row = tb.row(_rowKey(key));
await row.delete();
}
diff --git a/lib/src/syncbase/settings_manager.dart b/lib/src/syncbase/settings_manager.dart
index 790b43c..32f63a0 100644
--- a/lib/src/syncbase/settings_manager.dart
+++ b/lib/src/syncbase/settings_manager.dart
@@ -35,9 +35,7 @@
final CroupierClient _cc;
sc.SyncbaseTable tb;
- static const String _discoverySettingsKey = "settings";
- static const String _personalKey = "personal";
- static const String _settingsWatchSyncPrefix = "users";
+ static const String _discoveryGameAdKey = "discovery-game-ad";
SettingsManager(
settings_client.AppSettings appSettings,
@@ -47,15 +45,6 @@
this.updateGameStatusCallback)
: _cc = new CroupierClient(appSettings);
- String _settingsDataKey(int userID) {
- return "${_settingsWatchSyncPrefix}/${userID}/settings";
- }
-
- String _settingsDataKeyUserID(String dataKey) {
- List<String> parts = dataKey.split("/");
- return parts[parts.length - 2];
- }
-
Future _prepareSettingsTable() async {
if (tb != null) {
return; // Then we're already prepared.
@@ -65,8 +54,8 @@
tb = await _cc.createTable(db, util.tableNameSettings);
// Start to watch the stream for the shared settings table.
- Stream<sc.WatchChange> watchStream = db.watch(
- util.tableNameSettings, _settingsWatchSyncPrefix, UTF8.encode("now"));
+ Stream<sc.WatchChange> watchStream = db.watch(util.tableNameSettings,
+ util.settingsWatchSyncPrefix, UTF8.encode("now"));
_startWatchSettings(watchStream); // Don't wait for this future.
_loadSettings(tb); // Don't wait for this future.
}
@@ -84,7 +73,7 @@
await this.save(settings.userID, jsonStr);
return jsonStr;
} else {
- return await _tryReadData(tb, this._settingsDataKey(userID));
+ return await _tryReadData(tb, util.settingsDataKeyFromUserID(userID));
}
}
@@ -104,8 +93,10 @@
util.log('SettingsManager.save');
await _prepareSettingsTable();
- await tb.row(_personalKey).put(UTF8.encode("${userID}"));
- await tb.row(this._settingsDataKey(userID)).put(UTF8.encode(jsonString));
+ await tb.row(util.settingsPersonalKey).put(UTF8.encode("${userID}"));
+ await tb
+ .row(util.settingsDataKeyFromUserID(userID))
+ .put(UTF8.encode(jsonString));
}
// This watch method ensures that any changes are propagated to the caller.
@@ -132,7 +123,7 @@
}
if (this.updateSettingsCallback != null) {
- this.updateSettingsCallback(_settingsDataKeyUserID(key), value);
+ this.updateSettingsCallback(util.userIDFromSettingsDataKey(key), value);
}
}
}
@@ -143,7 +134,7 @@
_cc.createSyncgroup(
await _mySettingsSyncgroupName(), util.tableNameSettings,
- prefix: this._settingsDataKey(id));
+ prefix: util.settingsDataKeyFromUserID(id));
}
Future<String> _mySettingsSyncgroupName() async {
@@ -175,19 +166,18 @@
if (key.indexOf("/players") != -1) {
if (this.updatePlayerFoundCallback != null) {
- String playerID = _getPartFromBack(key, "/", 1);
- String type = _getPartFromBack(key, "/", 0);
+ String type = util.playerUpdateTypeFromPlayerKey(key);
switch (type) {
case "player_number":
// Update the player number for this player.
- this.updatePlayerFoundCallback(playerID, value);
+ this.updatePlayerFoundCallback(key, value);
break;
case "settings_sg":
// Join this player's settings syncgroup.
_cc.joinSyncgroup(value);
// Also, signal that this player has been found.
- this.updatePlayerFoundCallback(playerID, null);
+ this.updatePlayerFoundCallback(key, null);
break;
default:
print("Unexpected key: ${key} with value ${value}");
@@ -215,12 +205,12 @@
print("Now writing to some rows of ${gameID}");
// Start up the table and write yourself as player 0.
- await gameTable.row("${gameID}/type").put(UTF8.encode("${type}"));
+ await gameTable.row(util.gameTypeKey(gameID)).put(UTF8.encode("${type}"));
int id = await _getUserID();
- await gameTable.row("${gameID}/owner").put(UTF8.encode("${id}"));
+ await gameTable.row(util.gameOwnerKey(gameID)).put(UTF8.encode("${id}"));
await gameTable
- .row("${gameID}/players/${id}/settings_sg")
+ .row(util.playerSettingsKeyFromData(gameID, id))
.put(UTF8.encode(await _mySettingsSyncgroupName()));
logic_game.GameStartData gsd =
@@ -249,7 +239,7 @@
int id = await _getUserID();
await gameTable
- .row("${gameID}/players/${id}/settings_sg")
+ .row(util.playerSettingsKeyFromData(gameID, id))
.put(UTF8.encode(await _mySettingsSyncgroupName()));
}
@@ -259,7 +249,7 @@
int id = await _getUserID();
await gameTable
- .row("${gameID}/players/${id}/player_number")
+ .row(util.playerNumberKeyFromData(gameID, id))
.put(UTF8.encode("${playerNumber}"));
}
@@ -267,19 +257,19 @@
sc.SyncbaseDatabase db = await _cc.createDatabase();
sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
- await gameTable.row("${gameID}/status").put(UTF8.encode(status));
+ await gameTable.row(util.gameStatusKey(gameID)).put(UTF8.encode(status));
}
// When starting the settings manager, there may be settings already in the
// store. Make sure to load those.
Future _loadSettings(sc.SyncbaseTable tb) async {
tb
- .scan(new sc.RowRange.prefix(_settingsWatchSyncPrefix))
+ .scan(new sc.RowRange.prefix(util.settingsWatchSyncPrefix))
.forEach((sc.KeyValue kv) {
- if (kv.key.endsWith("/settings")) {
+ if (util.isSettingsKey(kv.key)) {
// Then we can process the value as if it were settings data.
this.updateSettingsCallback(
- _settingsDataKeyUserID(kv.key), UTF8.decode(kv.value));
+ util.userIDFromSettingsDataKey(kv.key), UTF8.decode(kv.value));
}
});
}
@@ -292,12 +282,12 @@
Future scanSettings() async {
SettingsScanHandler ssh =
new SettingsScanHandler(_cc, this.updateGamesCallback);
- return _cc.discoveryClient.scan(_discoverySettingsKey,
+ return _cc.discoveryClient.scan(_discoveryGameAdKey,
'v.InterfaceName="${util.discoveryInterfaceName}"', ssh);
}
Future stopScanSettings() {
- return _cc.discoveryClient.stopScan(_discoverySettingsKey);
+ return _cc.discoveryClient.stopScan(_discoveryGameAdKey);
}
// Someone who wants to join a game should advertise their presence.
@@ -305,7 +295,7 @@
String settingsSuffix = await _syncSettingsSuffix();
String gameSuffix = util.syncgameSuffix("${gsd.gameID}");
return _cc.discoveryClient.advertise(
- _discoverySettingsKey,
+ _discoveryGameAdKey,
DiscoveryClient.serviceMaker(
interfaceName: util.discoveryInterfaceName,
attrs: <String, String>{
@@ -316,11 +306,11 @@
}
Future stopAdvertiseSettings() {
- return _cc.discoveryClient.stopAdvertise(_discoverySettingsKey);
+ return _cc.discoveryClient.stopAdvertise(_discoveryGameAdKey);
}
Future<int> _getUserID() async {
- String result = await _tryReadData(tb, _personalKey);
+ String result = await _tryReadData(tb, util.settingsPersonalKey);
if (result == null) {
return null;
}
@@ -337,11 +327,6 @@
}
}
-String _getPartFromBack(String input, String separator, int indexFromLast) {
- List<String> parts = input.split(separator);
- return parts[parts.length - 1 - indexFromLast];
-}
-
// Implementation of the ScanHandler for Settings information.
// Upon finding a settings advertiser, you want to join the syncgroup that
// they're advertising.
diff --git a/lib/src/syncbase/util.dart b/lib/src/syncbase/util.dart
index 749e0cd..9523947 100644
--- a/lib/src/syncbase/util.dart
+++ b/lib/src/syncbase/util.dart
@@ -21,6 +21,9 @@
const String discoveryInterfaceName = 'CroupierSettingsAndGame';
+const String settingsPersonalKey = "personal";
+const String settingsWatchSyncPrefix = "users";
+
typedef void NoArgCb();
typedef void keyValueCallback(String key, String value);
@@ -44,3 +47,56 @@
const String syncgameSettingsAttr = "settings_sgname";
const String syncgameGameStartDataAttr = "game_start_data";
+
+const String separator = "/";
+
+String gameIDFromGameKey(String gameKey) {
+ List<String> parts = gameKey.split(separator);
+ return parts[0];
+}
+
+String playerUpdateTypeFromPlayerKey(String playerKey) {
+ return _getPartFromBack(playerKey, 0);
+}
+
+String playerIDFromPlayerKey(String playerKey) {
+ return _getPartFromBack(playerKey, 1);
+}
+
+String gameOwnerKey(int gameID) {
+ return "${gameID}/owner";
+}
+
+String gameTypeKey(int gameID) {
+ return "${gameID}/type";
+}
+
+String gameStatusKey(int gameID) {
+ return "${gameID}/status";
+}
+
+String playerSettingsKeyFromData(int gameID, int userID) {
+ return "${gameID}/players/${userID}/settings_sg";
+}
+
+String playerNumberKeyFromData(int gameID, int userID) {
+ return "${gameID}/players/${userID}/player_number";
+}
+
+bool isSettingsKey(String key) {
+ return key.indexOf(settingsWatchSyncPrefix) == 0 && key.endsWith("/settings");
+}
+
+String settingsDataKeyFromUserID(int userID) {
+ return "${settingsWatchSyncPrefix}/${userID}/settings";
+}
+
+String userIDFromSettingsDataKey(String dataKey) {
+ List<String> parts = dataKey.split("/");
+ return parts[parts.length - 2];
+}
+
+String _getPartFromBack(String input, int indexFromLast) {
+ List<String> parts = input.split(separator);
+ return parts[parts.length - 1 - indexFromLast];
+}